PostgreSQL Source Code git master
Loading...
Searching...
No Matches
stashfuncs.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * stashfuncs.c
4 * SQL interface to pg_stash_advice
5 *
6 * Copyright (c) 2016-2026, PostgreSQL Global Development Group
7 *
8 * contrib/pg_stash_advice/stashfuncs.c
9 *
10 *-------------------------------------------------------------------------
11 */
12#include "postgres.h"
13
14#include "common/hashfn.h"
15#include "fmgr.h"
16#include "funcapi.h"
17#include "miscadmin.h"
18#include "pg_stash_advice.h"
19#include "utils/builtins.h"
20#include "utils/tuplestore.h"
21
28
35
36#define SH_PREFIX pgsa_stash_count_table
37#define SH_ELEMENT_TYPE pgsa_stash_count
38#define SH_KEY_TYPE uint64
39#define SH_KEY pgsa_stash_id
40#define SH_HASH_KEY(tb, key) hash_bytes((const unsigned char *) &(key), sizeof(uint64))
41#define SH_EQUAL(tb, a, b) (a == b)
42#define SH_SCOPE static inline
43#define SH_DEFINE
44#define SH_DECLARE
45#include "lib/simplehash.h"
46
47/*
48 * SQL-callable function to create an advice stash
49 */
64
65/*
66 * SQL-callable function to drop an advice stash
67 */
82
83/*
84 * SQL-callable function to provide a list of advice stashes
85 */
88{
89 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
91 pgsa_entry *entry;
94
95 InitMaterializedSRF(fcinfo, 0);
96
97 /* Attach to dynamic shared memory if not already done. */
100
101 /* If stash data is still being restored from disk, ignore. */
103 return (Datum) 0;
104
105 /* Tally up the number of entries per stash. */
108 while ((entry = dshash_seq_next(&iterator)) != NULL)
109 {
111 bool found;
112
114 entry->key.pgsa_stash_id,
115 &found);
116 if (!found)
117 c->num_entries = 1;
118 else
119 c->num_entries++;
120 }
122
123 /* Emit results. */
125 while ((stash = dshash_seq_next(&iterator)) != NULL)
126 {
127 Datum values[2];
128 bool nulls[2];
130
131 values[0] = CStringGetTextDatum(stash->name);
132 nulls[0] = false;
133
134 c = pgsa_stash_count_table_lookup(chash, stash->pgsa_stash_id);
135 values[1] = Int64GetDatum(c == NULL ? 0 : c->num_entries);
136 nulls[1] = false;
137
138 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values,
139 nulls);
140 }
142
143 return (Datum) 0;
144}
145
146/*
147 * SQL-callable function to provide advice stash contents
148 */
149Datum
151{
152 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
154 char *stash_name = NULL;
156 uint64 stash_id = 0;
157 pgsa_entry *entry;
158
159 InitMaterializedSRF(fcinfo, 0);
160
161 /* Attach to dynamic shared memory if not already done. */
163 pgsa_attach();
164
165 /* If stash data is still being restored from disk, ignore. */
167 return (Datum) 0;
168
169 /* User can pass NULL for all stashes, or the name of a specific stash. */
170 if (!PG_ARGISNULL(0))
171 {
172 stash_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
173 pgsa_check_stash_name(stash_name);
174 stash_id = pgsa_lookup_stash_id(stash_name);
175
176 /* If the user specified a stash name, it should exist. */
177 if (stash_id == 0)
180 errmsg("advice stash \"%s\" does not exist", stash_name));
181 }
182 else
183 {
185
186 /*
187 * If we're dumping data about all stashes, we need an ID->name lookup
188 * table.
189 */
192 while ((stash = dshash_seq_next(&iterator)) != NULL)
193 {
195 bool found;
196
198 stash->pgsa_stash_id,
199 &found);
200 Assert(!found);
201 n->name = pstrdup(stash->name);
202 }
204 }
205
206 /* Now iterate over all the entries. */
208 while ((entry = dshash_seq_next(&iterator)) != NULL)
209 {
210 Datum values[3];
211 bool nulls[3];
212 char *this_stash_name;
213 char *advice_string;
214
215 /* Skip incomplete entries where the advice string was never set. */
216 if (entry->advice_string == InvalidDsaPointer)
217 continue;
218
219 if (stash_id != 0)
220 {
221 /*
222 * We're only dumping data for one particular stash, so skip
223 * entries for any other stash and use the stash name specified by
224 * the user.
225 */
226 if (stash_id != entry->key.pgsa_stash_id)
227 continue;
228 this_stash_name = stash_name;
229 }
230 else
231 {
233
234 /*
235 * We're dumping data for all stashes, so look up the correct name
236 * to use in the hash table. If nothing is found, which is
237 * possible due to race conditions, make up a string to use.
238 */
240 if (n != NULL)
242 else
243 this_stash_name = psprintf("<stash %" PRIu64 ">",
244 entry->key.pgsa_stash_id);
245 }
246
247 /* Work out tuple values. */
249 nulls[0] = false;
250 values[1] = Int64GetDatum(entry->key.queryId);
251 nulls[1] = false;
252 advice_string = dsa_get_address(pgsa_dsa_area, entry->advice_string);
253 values[2] = CStringGetTextDatum(advice_string);
254 nulls[2] = false;
255
256 /* Emit the tuple. */
257 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values,
258 nulls);
259 }
261
262 return (Datum) 0;
263}
264
265/*
266 * SQL-callable function to update an advice stash entry for a particular
267 * query ID
268 *
269 * If the second argument is NULL, we delete any existing advice stash
270 * entry; otherwise, we either create an entry or update it with the new
271 * advice string.
272 */
273Datum
275{
276 char *stash_name;
277 int64 queryId;
278
279 if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
281
282 /* Get and check advice stash name. */
283 stash_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
284 pgsa_check_stash_name(stash_name);
285
286 /*
287 * Get and check query ID.
288 *
289 * Query ID 0 means no query ID was computed, so reject that.
290 */
291 queryId = PG_GETARG_INT64(1);
292 if (queryId == 0)
295 errmsg("cannot set advice string for query ID 0"));
296
297 /* Attach to dynamic shared memory if not already done. */
299 pgsa_attach();
300
301 /* Don't allow writes if stash data is still being restored from disk. */
303
304 /* Now call the appropriate function to do the real work. */
305 if (PG_ARGISNULL(2))
306 {
308 pgsa_clear_advice_string(stash_name, queryId);
310 }
311 else
312 {
313 char *advice_string = text_to_cstring(PG_GETARG_TEXT_PP(2));
314
316 pgsa_set_advice_string(stash_name, queryId, advice_string);
318 }
319
321}
322
323/*
324 * SQL-callable function to start the persistence background worker.
325 */
326Datum
328{
329 pid_t pid;
330
332 pgsa_attach();
333
337
338 if (pid != InvalidPid)
341 errmsg("pg_stash_advice worker is already running under PID %d",
342 (int) pid)));
343
345
347}
static bool pg_atomic_unlocked_test_flag(volatile pg_atomic_flag *ptr)
Definition atomics.h:194
static Datum values[MAXATTR]
Definition bootstrap.c:190
#define CStringGetTextDatum(s)
Definition builtins.h:98
#define Assert(condition)
Definition c.h:943
int64_t int64
Definition c.h:621
uint64_t uint64
Definition c.h:625
#define unlikely(x)
Definition c.h:438
uint32_t uint32
Definition c.h:624
void * dsa_get_address(dsa_area *area, dsa_pointer dp)
Definition dsa.c:957
#define InvalidDsaPointer
Definition dsa.h:78
void dshash_seq_init(dshash_seq_status *status, dshash_table *hash_table, bool exclusive)
Definition dshash.c:659
void dshash_seq_term(dshash_seq_status *status)
Definition dshash.c:768
void * dshash_seq_next(dshash_seq_status *status)
Definition dshash.c:678
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
#define ereport(elevel,...)
Definition elog.h:152
#define PG_RETURN_VOID()
Definition fmgr.h:350
#define PG_GETARG_TEXT_PP(n)
Definition fmgr.h:310
#define PG_ARGISNULL(n)
Definition fmgr.h:209
#define PG_RETURN_NULL()
Definition fmgr.h:346
#define PG_GETARG_INT64(n)
Definition fmgr.h:284
#define PG_FUNCTION_INFO_V1(funcname)
Definition fmgr.h:417
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
void InitMaterializedSRF(FunctionCallInfo fcinfo, uint32 flags)
Definition funcapi.c:76
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1150
void LWLockRelease(LWLock *lock)
Definition lwlock.c:1767
@ LW_SHARED
Definition lwlock.h:105
@ LW_EXCLUSIVE
Definition lwlock.h:104
char * pstrdup(const char *in)
Definition mcxt.c:1781
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
#define InvalidPid
Definition miscadmin.h:32
static char * errmsg
dshash_table * pgsa_entry_dshash
dshash_table * pgsa_stash_dshash
void pgsa_create_stash(char *stash_name)
void pgsa_drop_stash(char *stash_name)
void pgsa_set_advice_string(char *stash_name, int64 queryId, char *advice_string)
void pgsa_start_worker(void)
dsa_area * pgsa_dsa_area
uint64 pgsa_lookup_stash_id(char *stash_name)
void pgsa_attach(void)
void pgsa_check_stash_name(char *stash_name)
pgsa_shared_state * pgsa_state
void pgsa_clear_advice_string(char *stash_name, int64 queryId)
void pgsa_check_lockout(void)
static Datum Int64GetDatum(int64 X)
Definition postgres.h:426
uint64_t Datum
Definition postgres.h:70
char * c
static int fb(int x)
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
Datum pg_get_advice_stash_contents(PG_FUNCTION_ARGS)
Definition stashfuncs.c:150
Datum pg_start_stash_advice_worker(PG_FUNCTION_ARGS)
Definition stashfuncs.c:327
Datum pg_get_advice_stashes(PG_FUNCTION_ARGS)
Definition stashfuncs.c:87
Datum pg_drop_advice_stash(PG_FUNCTION_ARGS)
Definition stashfuncs.c:69
Datum pg_create_advice_stash(PG_FUNCTION_ARGS)
Definition stashfuncs.c:51
Datum pg_set_stashed_advice(PG_FUNCTION_ARGS)
Definition stashfuncs.c:274
int64 queryId
uint64 pgsa_stash_id
pgsa_entry_key key
dsa_pointer advice_string
pg_atomic_flag stashes_ready
uint64 pgsa_stash_id
Definition stashfuncs.c:32
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition tuplestore.c:785
char * text_to_cstring(const text *t)
Definition varlena.c:217