PostgreSQL Source Code git master
Loading...
Searching...
No Matches
explain_state.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * explain_state.c
4 * Code for initializing and accessing ExplainState objects
5 *
6 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994-5, Regents of the University of California
8 *
9 * In-core options have hard-coded fields inside ExplainState; e.g. if
10 * the user writes EXPLAIN (BUFFERS) then ExplainState's "buffers" member
11 * will be set to true. Extensions can also register options using
12 * RegisterExtensionExplainOption; so that e.g. EXPLAIN (BICYCLE 'red')
13 * will invoke a designated handler that knows what the legal values are
14 * for the BICYCLE option. However, it's not enough for an extension to be
15 * able to parse new options: it also needs a place to store the results
16 * of that parsing, and an ExplainState has no 'bicycle' field.
17 *
18 * To solve this problem, an ExplainState can contain an array of opaque
19 * pointers, one per extension. An extension can use GetExplainExtensionId
20 * to acquire an integer ID to acquire an offset into this array that is
21 * reserved for its exclusive use, and then use GetExplainExtensionState
22 * and SetExplainExtensionState to read and write its own private state
23 * within an ExplainState.
24 *
25 * Note that there is no requirement that the name of the option match
26 * the name of the extension; e.g. a pg_explain_conveyance extension could
27 * implement options for BICYCLE, MONORAIL, etc.
28 *
29 * IDENTIFICATION
30 * src/backend/commands/explain_state.c
31 *
32 *-------------------------------------------------------------------------
33 */
34#include "postgres.h"
35
36#include "commands/defrem.h"
37#include "commands/explain.h"
39#include "utils/builtins.h"
40#include "utils/guc.h"
41
42/* Hook to perform additional EXPLAIN options validation */
44
51
52static const char **ExplainExtensionNameArray = NULL;
55
59
60/*
61 * Create a new ExplainState struct initialized with default options.
62 */
65{
67
68 /* Set default options (most fields can be left as zeroes). */
69 es->costs = true;
70 /* Prepare output buffer. */
71 es->str = makeStringInfo();
72
73 return es;
74}
75
76/*
77 * Parse a list of EXPLAIN options and update an ExplainState accordingly.
78 */
79void
81{
82 ListCell *lc;
83 bool timing_set = false;
84 bool buffers_set = false;
85 bool summary_set = false;
86
87 /* Parse options list. */
88 foreach(lc, options)
89 {
90 DefElem *opt = (DefElem *) lfirst(lc);
91
92 if (strcmp(opt->defname, "analyze") == 0)
93 es->analyze = defGetBoolean(opt);
94 else if (strcmp(opt->defname, "verbose") == 0)
95 es->verbose = defGetBoolean(opt);
96 else if (strcmp(opt->defname, "costs") == 0)
97 es->costs = defGetBoolean(opt);
98 else if (strcmp(opt->defname, "buffers") == 0)
99 {
100 buffers_set = true;
101 es->buffers = defGetBoolean(opt);
102 }
103 else if (strcmp(opt->defname, "wal") == 0)
104 es->wal = defGetBoolean(opt);
105 else if (strcmp(opt->defname, "settings") == 0)
106 es->settings = defGetBoolean(opt);
107 else if (strcmp(opt->defname, "generic_plan") == 0)
108 es->generic = defGetBoolean(opt);
109 else if (strcmp(opt->defname, "timing") == 0)
110 {
111 timing_set = true;
112 es->timing = defGetBoolean(opt);
113 }
114 else if (strcmp(opt->defname, "summary") == 0)
115 {
116 summary_set = true;
117 es->summary = defGetBoolean(opt);
118 }
119 else if (strcmp(opt->defname, "memory") == 0)
120 es->memory = defGetBoolean(opt);
121 else if (strcmp(opt->defname, "serialize") == 0)
122 {
123 if (opt->arg)
124 {
125 char *p = defGetString(opt);
126
127 if (strcmp(p, "off") == 0 || strcmp(p, "none") == 0)
129 else if (strcmp(p, "text") == 0)
131 else if (strcmp(p, "binary") == 0)
133 else
136 errmsg("unrecognized value for %s option \"%s\": \"%s\"",
137 "EXPLAIN", opt->defname, p),
138 parser_errposition(pstate, opt->location)));
139 }
140 else
141 {
142 /* SERIALIZE without an argument is taken as 'text' */
144 }
145 }
146 else if (strcmp(opt->defname, "format") == 0)
147 {
148 char *p = defGetString(opt);
149
150 if (strcmp(p, "text") == 0)
152 else if (strcmp(p, "xml") == 0)
154 else if (strcmp(p, "json") == 0)
156 else if (strcmp(p, "yaml") == 0)
158 else
161 errmsg("unrecognized value for %s option \"%s\": \"%s\"",
162 "EXPLAIN", opt->defname, p),
163 parser_errposition(pstate, opt->location)));
164 }
165 else if (strcmp(opt->defname, "io") == 0)
166 es->io = defGetBoolean(opt);
167 else if (!ApplyExtensionExplainOption(es, opt, pstate))
170 errmsg("unrecognized %s option \"%s\"",
171 "EXPLAIN", opt->defname),
172 parser_errposition(pstate, opt->location)));
173 }
174
175 /* check that WAL is used with EXPLAIN ANALYZE */
176 if (es->wal && !es->analyze)
179 errmsg("EXPLAIN option %s requires ANALYZE", "WAL")));
180
181 /* if the timing was not set explicitly, set default value */
182 es->timing = (timing_set) ? es->timing : es->analyze;
183
184 /* if the buffers was not set explicitly, set default value */
185 es->buffers = (buffers_set) ? es->buffers : es->analyze;
186
187 /* check that timing is used with EXPLAIN ANALYZE */
188 if (es->timing && !es->analyze)
191 errmsg("EXPLAIN option %s requires ANALYZE", "TIMING")));
192
193 /* check that IO is used with EXPLAIN ANALYZE */
194 if (es->io && !es->analyze)
197 errmsg("EXPLAIN option %s requires ANALYZE", "IO")));
198
199 /* check that serialize is used with EXPLAIN ANALYZE */
200 if (es->serialize != EXPLAIN_SERIALIZE_NONE && !es->analyze)
203 errmsg("EXPLAIN option %s requires ANALYZE", "SERIALIZE")));
204
205 /* check that GENERIC_PLAN is not used with EXPLAIN ANALYZE */
206 if (es->generic && es->analyze)
209 errmsg("%s options %s and %s cannot be used together",
210 "EXPLAIN", "ANALYZE", "GENERIC_PLAN")));
211
212 /* if the summary was not set explicitly, set default value */
213 es->summary = (summary_set) ? es->summary : es->analyze;
214
215 /* plugin specific option validation */
217 (*explain_validate_options_hook) (es, options, pstate);
218}
219
220/*
221 * Map the name of an EXPLAIN extension to an integer ID.
222 *
223 * Within the lifetime of a particular backend, the same name will be mapped
224 * to the same ID every time. IDs are not stable across backends. Use the ID
225 * that you get from this function to call GetExplainExtensionState and
226 * SetExplainExtensionState.
227 *
228 * extension_name is assumed to be a constant string or allocated in storage
229 * that will never be freed.
230 */
231int
233{
234 /* Search for an existing extension by this name; if found, return ID. */
235 for (int i = 0; i < ExplainExtensionNamesAssigned; ++i)
237 return i;
238
239 /* If there is no array yet, create one. */
241 {
243 ExplainExtensionNameArray = (const char **)
246 * sizeof(char *));
247 }
248
249 /* If there's an array but it's currently full, expand it. */
251 {
253
254 ExplainExtensionNameArray = (const char **)
255 repalloc(ExplainExtensionNameArray, i * sizeof(char *));
257 }
258
259 /* Assign and return new ID. */
262}
263
264/*
265 * Get extension-specific state from an ExplainState.
266 *
267 * See comments for SetExplainExtensionState, below.
268 */
269void *
271{
272 Assert(extension_id >= 0);
273
275 return NULL;
276
277 return es->extension_state[extension_id];
278}
279
280/*
281 * Store extension-specific state into an ExplainState.
282 *
283 * To use this function, first obtain an integer extension_id using
284 * GetExplainExtensionId. Then use this function to store an opaque pointer
285 * in the ExplainState. Later, you can retrieve the opaque pointer using
286 * GetExplainExtensionState.
287 */
288void
290{
291 Assert(extension_id >= 0);
292
293 /* If there is no array yet, create one. */
294 if (es->extension_state == NULL)
295 {
298 es->extension_state =
299 palloc0(es->extension_state_allocated * sizeof(void *));
300 }
301
302 /* If there's an array but it's currently full, expand it. */
304 {
305 int i;
306
310 }
311
312 es->extension_state[extension_id] = opaque;
313}
314
315/*
316 * Register a new EXPLAIN option.
317 *
318 * option_name is assumed to be a constant string or allocated in storage
319 * that will never be freed.
320 *
321 * When option_name is used as an EXPLAIN option, handler will be called and
322 * should update the ExplainState passed to it. See comments at top of file
323 * for a more detailed explanation.
324 *
325 * guc_check_handler is a function that can be safely called from a
326 * GUC check hook to validate a proposed value for a custom EXPLAIN option.
327 * Boolean-valued options can pass GUCCheckBooleanExplainOption. See the
328 * comments for GUCCheckBooleanExplainOption for further information on
329 * how a guc_check_handler should behave.
330 */
331void
332RegisterExtensionExplainOption(const char *option_name,
333 ExplainOptionHandler handler,
334 ExplainOptionGUCCheckHandler guc_check_handler)
335{
337
338 Assert(handler != NULL);
339 Assert(guc_check_handler != NULL);
340
341 /* Search for an existing option by this name; if found, update handler. */
342 for (int i = 0; i < ExplainExtensionOptionsAssigned; ++i)
343 {
344 if (strcmp(ExplainExtensionOptionArray[i].option_name,
345 option_name) == 0)
346 {
348
349 exopt->option_handler = handler;
350 exopt->guc_check_handler = guc_check_handler;
351 return;
352 }
353 }
354
355 /* If there is no array yet, create one. */
357 {
362 * sizeof(ExplainExtensionOption));
363 }
364
365 /* If there's an array but it's currently full, expand it. */
367 {
369
373 }
374
375 /* Assign and return new ID. */
377 exopt->option_name = option_name;
378 exopt->option_handler = handler;
379 exopt->guc_check_handler = guc_check_handler;
380}
381
382/*
383 * Apply an EXPLAIN option registered by an extension.
384 *
385 * If no extension has registered the named option, returns false. Otherwise,
386 * calls the appropriate handler function and then returns true.
387 */
388bool
390{
391 for (int i = 0; i < ExplainExtensionOptionsAssigned; ++i)
392 {
393 if (strcmp(ExplainExtensionOptionArray[i].option_name,
394 opt->defname) == 0)
395 {
397 return true;
398 }
399 }
400
401 return false;
402}
403
404/*
405 * Determine whether an EXPLAIN extension option will be accepted without
406 * error. Returns true if so, and false if not. See the comments for
407 * GUCCheckBooleanExplainOption for more details.
408 *
409 * The caller need not know that the option_name is valid; this function
410 * will indicate that the option is unrecognized if that is the case.
411 */
412bool
413GUCCheckExplainExtensionOption(const char *option_name,
414 const char *option_value,
416{
417 for (int i = 0; i < ExplainExtensionOptionsAssigned; i++)
418 {
420
421 if (strcmp(exopt->option_name, option_name) == 0)
422 return exopt->guc_check_handler(option_name, option_value,
424 }
425
426 /* Unrecognized option name. */
427 GUC_check_errmsg("unrecognized EXPLAIN option \"%s\"", option_name);
428 return false;
429}
430
431/*
432 * guc_check_handler for Boolean-valued EXPLAIN extension options.
433 *
434 * After receiving a "true" value from this or any other GUC check handler
435 * for an EXPLAIN extension option, the caller is entitled to assume that
436 * a suitably constructed DefElem passed to the main option handler will
437 * not cause an error. To construct this DefElem, the caller should set
438 * the DefElem's defname to option_name. If option_values is NULL, arg
439 * should be NULL. Otherwise, arg should be of the type given by
440 * option_type, with option_value as the associated value. The only option
441 * types that should be passed are T_String, T_Float, and T_Integer; in
442 * the last case, the caller will need to perform a string-to-integer
443 * conversion.
444 *
445 * A guc_check_handler should not throw an error, and should not allocate
446 * memory. If it returns false to indicate that the option_value is not
447 * acceptable, it may use GUC_check_errmsg(), GUC_check_errdetail(), etc.
448 * to clarify the nature of the problem.
449 *
450 * Since we're concerned with Boolean options here, the logic below must
451 * exactly match the semantics of defGetBoolean.
452 */
453bool
454GUCCheckBooleanExplainOption(const char *option_name,
455 const char *option_value,
457{
458 bool valid = false;
459
460 if (option_value == NULL)
461 {
462 /* defGetBoolean treats no argument as valid */
463 valid = true;
464 }
465 else if (option_type == T_String)
466 {
467 /* defGetBoolean accepts exactly these string values */
468 if (pg_strcasecmp(option_value, "true") == 0 ||
469 pg_strcasecmp(option_value, "false") == 0 ||
470 pg_strcasecmp(option_value, "on") == 0 ||
471 pg_strcasecmp(option_value, "off") == 0)
472 valid = true;
473 }
474 else if (option_type == T_Integer)
475 {
476 long value;
477 char *end;
478
479 /*
480 * defGetBoolean accepts only 0 and 1, but those can be spelled in
481 * various ways (e.g. 01, 0x01).
482 */
483 errno = 0;
484 value = strtol(option_value, &end, 0);
485 if (errno == 0 && *end == '\0' && end != option_value &&
486 value == (int) value && (value == 0 || value == 1))
487 valid = true;
488 }
489
490 if (!valid)
491 {
492 GUC_check_errmsg("EXPLAIN option \"%s\" requires a Boolean value",
493 option_name);
494 return false;
495 }
496
497 return true;
498}
#define Max(x, y)
Definition c.h:1085
#define Assert(condition)
Definition c.h:943
char * defGetString(DefElem *def)
Definition define.c:34
bool defGetBoolean(DefElem *def)
Definition define.c:93
int errcode(int sqlerrcode)
Definition elog.c:874
#define ERROR
Definition elog.h:40
#define ereport(elevel,...)
Definition elog.h:152
static ExplainExtensionOption * ExplainExtensionOptionArray
ExplainState * NewExplainState(void)
static const char ** ExplainExtensionNameArray
bool GUCCheckExplainExtensionOption(const char *option_name, const char *option_value, NodeTag option_type)
static int ExplainExtensionNamesAssigned
int GetExplainExtensionId(const char *extension_name)
void * GetExplainExtensionState(ExplainState *es, int extension_id)
static int ExplainExtensionNamesAllocated
void SetExplainExtensionState(ExplainState *es, int extension_id, void *opaque)
void RegisterExtensionExplainOption(const char *option_name, ExplainOptionHandler handler, ExplainOptionGUCCheckHandler guc_check_handler)
static int ExplainExtensionOptionsAllocated
bool GUCCheckBooleanExplainOption(const char *option_name, const char *option_value, NodeTag option_type)
explain_validate_options_hook_type explain_validate_options_hook
void ParseExplainOptionList(ExplainState *es, List *options, ParseState *pstate)
bool ApplyExtensionExplainOption(ExplainState *es, DefElem *opt, ParseState *pstate)
static int ExplainExtensionOptionsAssigned
@ EXPLAIN_SERIALIZE_TEXT
@ EXPLAIN_SERIALIZE_NONE
@ EXPLAIN_SERIALIZE_BINARY
void(* ExplainOptionHandler)(ExplainState *, DefElem *, ParseState *)
@ EXPLAIN_FORMAT_XML
@ EXPLAIN_FORMAT_YAML
@ EXPLAIN_FORMAT_TEXT
@ EXPLAIN_FORMAT_JSON
void(* explain_validate_options_hook_type)(ExplainState *es, List *options, ParseState *pstate)
bool(* ExplainOptionGUCCheckHandler)(const char *option_name, const char *option_value, NodeTag option_type)
#define palloc0_object(type)
Definition fe_memutils.h:75
#define GUC_check_errmsg
Definition guc.h:503
static struct @177 value
int i
Definition isn.c:77
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1232
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1632
void * palloc0(Size size)
Definition mcxt.c:1417
MemoryContext TopMemoryContext
Definition mcxt.c:166
NodeTag
Definition nodes.h:27
static char * errmsg
#define repalloc0_array(pointer, type, oldcount, count)
Definition palloc.h:109
int parser_errposition(ParseState *pstate, int location)
Definition parse_node.c:106
static uint32 pg_nextpower2_32(uint32 num)
#define lfirst(lc)
Definition pg_list.h:172
int pg_strcasecmp(const char *s1, const char *s2)
static int fb(int x)
static enum slash_option_type option_type
StringInfo makeStringInfo(void)
Definition stringinfo.c:72
char * defname
Definition parsenodes.h:860
ParseLoc location
Definition parsenodes.h:864
Node * arg
Definition parsenodes.h:861
ExplainOptionGUCCheckHandler guc_check_handler
ExplainOptionHandler option_handler
int extension_state_allocated
StringInfo str
void ** extension_state
ExplainFormat format
ExplainSerializeOption serialize
Definition pg_list.h:54