PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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-2025, 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
40/* Hook to perform additional EXPLAIN options validation */
42
43typedef struct
44{
45 const char *option_name;
48
49static const char **ExplainExtensionNameArray = NULL;
52
56
57/*
58 * Create a new ExplainState struct initialized with default options.
59 */
62{
64
65 /* Set default options (most fields can be left as zeroes). */
66 es->costs = true;
67 /* Prepare output buffer. */
68 es->str = makeStringInfo();
69
70 return es;
71}
72
73/*
74 * Parse a list of EXPLAIN options and update an ExplainState accordingly.
75 */
76void
78{
79 ListCell *lc;
80 bool timing_set = false;
81 bool buffers_set = false;
82 bool summary_set = false;
83
84 /* Parse options list. */
85 foreach(lc, options)
86 {
87 DefElem *opt = (DefElem *) lfirst(lc);
88
89 if (strcmp(opt->defname, "analyze") == 0)
90 es->analyze = defGetBoolean(opt);
91 else if (strcmp(opt->defname, "verbose") == 0)
92 es->verbose = defGetBoolean(opt);
93 else if (strcmp(opt->defname, "costs") == 0)
94 es->costs = defGetBoolean(opt);
95 else if (strcmp(opt->defname, "buffers") == 0)
96 {
97 buffers_set = true;
98 es->buffers = defGetBoolean(opt);
99 }
100 else if (strcmp(opt->defname, "wal") == 0)
101 es->wal = defGetBoolean(opt);
102 else if (strcmp(opt->defname, "settings") == 0)
103 es->settings = defGetBoolean(opt);
104 else if (strcmp(opt->defname, "generic_plan") == 0)
105 es->generic = defGetBoolean(opt);
106 else if (strcmp(opt->defname, "timing") == 0)
107 {
108 timing_set = true;
109 es->timing = defGetBoolean(opt);
110 }
111 else if (strcmp(opt->defname, "summary") == 0)
112 {
113 summary_set = true;
114 es->summary = defGetBoolean(opt);
115 }
116 else if (strcmp(opt->defname, "memory") == 0)
117 es->memory = defGetBoolean(opt);
118 else if (strcmp(opt->defname, "serialize") == 0)
119 {
120 if (opt->arg)
121 {
122 char *p = defGetString(opt);
123
124 if (strcmp(p, "off") == 0 || strcmp(p, "none") == 0)
126 else if (strcmp(p, "text") == 0)
128 else if (strcmp(p, "binary") == 0)
130 else
132 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
133 errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
134 opt->defname, p),
135 parser_errposition(pstate, opt->location)));
136 }
137 else
138 {
139 /* SERIALIZE without an argument is taken as 'text' */
141 }
142 }
143 else if (strcmp(opt->defname, "format") == 0)
144 {
145 char *p = defGetString(opt);
146
147 if (strcmp(p, "text") == 0)
149 else if (strcmp(p, "xml") == 0)
151 else if (strcmp(p, "json") == 0)
153 else if (strcmp(p, "yaml") == 0)
155 else
157 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
158 errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
159 opt->defname, p),
160 parser_errposition(pstate, opt->location)));
161 }
162 else if (!ApplyExtensionExplainOption(es, opt, pstate))
164 (errcode(ERRCODE_SYNTAX_ERROR),
165 errmsg("unrecognized EXPLAIN option \"%s\"",
166 opt->defname),
167 parser_errposition(pstate, opt->location)));
168 }
169
170 /* check that WAL is used with EXPLAIN ANALYZE */
171 if (es->wal && !es->analyze)
173 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
174 errmsg("EXPLAIN option %s requires ANALYZE", "WAL")));
175
176 /* if the timing was not set explicitly, set default value */
177 es->timing = (timing_set) ? es->timing : es->analyze;
178
179 /* if the buffers was not set explicitly, set default value */
180 es->buffers = (buffers_set) ? es->buffers : es->analyze;
181
182 /* check that timing is used with EXPLAIN ANALYZE */
183 if (es->timing && !es->analyze)
185 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
186 errmsg("EXPLAIN option %s requires ANALYZE", "TIMING")));
187
188 /* check that serialize is used with EXPLAIN ANALYZE */
189 if (es->serialize != EXPLAIN_SERIALIZE_NONE && !es->analyze)
191 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
192 errmsg("EXPLAIN option %s requires ANALYZE", "SERIALIZE")));
193
194 /* check that GENERIC_PLAN is not used with EXPLAIN ANALYZE */
195 if (es->generic && es->analyze)
197 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
198 errmsg("EXPLAIN options ANALYZE and GENERIC_PLAN cannot be used together")));
199
200 /* if the summary was not set explicitly, set default value */
201 es->summary = (summary_set) ? es->summary : es->analyze;
202
203 /* plugin specific option validation */
205 (*explain_validate_options_hook) (es, options, pstate);
206}
207
208/*
209 * Map the name of an EXPLAIN extension to an integer ID.
210 *
211 * Within the lifetime of a particular backend, the same name will be mapped
212 * to the same ID every time. IDs are not stable across backends. Use the ID
213 * that you get from this function to call GetExplainExtensionState and
214 * SetExplainExtensionState.
215 *
216 * extension_name is assumed to be a constant string or allocated in storage
217 * that will never be freed.
218 */
219int
220GetExplainExtensionId(const char *extension_name)
221{
222 /* Search for an existing extension by this name; if found, return ID. */
223 for (int i = 0; i < ExplainExtensionNamesAssigned; ++i)
224 if (strcmp(ExplainExtensionNameArray[i], extension_name) == 0)
225 return i;
226
227 /* If there is no array yet, create one. */
228 if (ExplainExtensionNameArray == NULL)
229 {
231 ExplainExtensionNameArray = (const char **)
234 * sizeof(char *));
235 }
236
237 /* If there's an array but it's currently full, expand it. */
239 {
241
242 ExplainExtensionNameArray = (const char **)
243 repalloc(ExplainExtensionNameArray, i * sizeof(char *));
245 }
246
247 /* Assign and return new ID. */
250}
251
252/*
253 * Get extension-specific state from an ExplainState.
254 *
255 * See comments for SetExplainExtensionState, below.
256 */
257void *
259{
260 Assert(extension_id >= 0);
261
262 if (extension_id >= es->extension_state_allocated)
263 return NULL;
264
265 return es->extension_state[extension_id];
266}
267
268/*
269 * Store extension-specific state into an ExplainState.
270 *
271 * To use this function, first obtain an integer extension_id using
272 * GetExplainExtensionId. Then use this function to store an opaque pointer
273 * in the ExplainState. Later, you can retrieve the opaque pointer using
274 * GetExplainExtensionState.
275 */
276void
277SetExplainExtensionState(ExplainState *es, int extension_id, void *opaque)
278{
279 Assert(extension_id >= 0);
280
281 /* If there is no array yet, create one. */
282 if (es->extension_state == NULL)
283 {
285 es->extension_state =
286 palloc0(es->extension_state_allocated * sizeof(void *));
287 }
288
289 /* If there's an array but it's currently full, expand it. */
290 if (extension_id >= es->extension_state_allocated)
291 {
292 int i;
293
295 es->extension_state = (void **)
297 es->extension_state_allocated * sizeof(void *),
298 i * sizeof(void *));
300 }
301
302 es->extension_state[extension_id] = opaque;
303}
304
305/*
306 * Register a new EXPLAIN option.
307 *
308 * When option_name is used as an EXPLAIN option, handler will be called and
309 * should update the ExplainState passed to it. See comments at top of file
310 * for a more detailed explanation.
311 *
312 * option_name is assumed to be a constant string or allocated in storage
313 * that will never be freed.
314 */
315void
316RegisterExtensionExplainOption(const char *option_name,
317 ExplainOptionHandler handler)
318{
320
321 /* Search for an existing option by this name; if found, update handler. */
322 for (int i = 0; i < ExplainExtensionOptionsAssigned; ++i)
323 {
324 if (strcmp(ExplainExtensionOptionArray[i].option_name,
325 option_name) == 0)
326 {
328 return;
329 }
330 }
331
332 /* If there is no array yet, create one. */
333 if (ExplainExtensionOptionArray == NULL)
334 {
339 * sizeof(char *));
340 }
341
342 /* If there's an array but it's currently full, expand it. */
344 {
346
348 repalloc(ExplainExtensionOptionArray, i * sizeof(char *));
350 }
351
352 /* Assign and return new ID. */
354 exopt->option_name = option_name;
355 exopt->option_handler = handler;
356}
357
358/*
359 * Apply an EXPLAIN option registered by an extension.
360 *
361 * If no extension has registered the named option, returns false. Otherwise,
362 * calls the appropriate handler function and then returns true.
363 */
364bool
366{
367 for (int i = 0; i < ExplainExtensionOptionsAssigned; ++i)
368 {
369 if (strcmp(ExplainExtensionOptionArray[i].option_name,
370 opt->defname) == 0)
371 {
373 return true;
374 }
375 }
376
377 return false;
378}
char * defGetString(DefElem *def)
Definition: define.c:35
bool defGetBoolean(DefElem *def)
Definition: define.c:94
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
static ExplainExtensionOption * ExplainExtensionOptionArray
Definition: explain_state.c:53
ExplainState * NewExplainState(void)
Definition: explain_state.c:61
static const char ** ExplainExtensionNameArray
Definition: explain_state.c:49
static int ExplainExtensionNamesAssigned
Definition: explain_state.c:50
int GetExplainExtensionId(const char *extension_name)
void * GetExplainExtensionState(ExplainState *es, int extension_id)
static int ExplainExtensionNamesAllocated
Definition: explain_state.c:51
void SetExplainExtensionState(ExplainState *es, int extension_id, void *opaque)
void RegisterExtensionExplainOption(const char *option_name, ExplainOptionHandler handler)
static int ExplainExtensionOptionsAllocated
Definition: explain_state.c:55
explain_validate_options_hook_type explain_validate_options_hook
Definition: explain_state.c:41
void ParseExplainOptionList(ExplainState *es, List *options, ParseState *pstate)
Definition: explain_state.c:77
bool ApplyExtensionExplainOption(ExplainState *es, DefElem *opt, ParseState *pstate)
static int ExplainExtensionOptionsAssigned
Definition: explain_state.c:54
@ EXPLAIN_SERIALIZE_TEXT
Definition: explain_state.h:23
@ EXPLAIN_SERIALIZE_NONE
Definition: explain_state.h:22
@ EXPLAIN_SERIALIZE_BINARY
Definition: explain_state.h:24
void(* ExplainOptionHandler)(ExplainState *, DefElem *, ParseState *)
Definition: explain_state.h:79
void(* explain_validate_options_hook_type)(struct ExplainState *es, List *options, ParseState *pstate)
Definition: explain_state.h:82
@ EXPLAIN_FORMAT_XML
Definition: explain_state.h:30
@ EXPLAIN_FORMAT_YAML
Definition: explain_state.h:32
@ EXPLAIN_FORMAT_TEXT
Definition: explain_state.h:29
@ EXPLAIN_FORMAT_JSON
Definition: explain_state.h:31
Assert(PointerIsAligned(start, uint64))
int i
Definition: isn.c:77
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
void * repalloc0(void *pointer, Size oldsize, Size size)
Definition: mcxt.c:1622
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1181
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1544
void * palloc0(Size size)
Definition: mcxt.c:1347
MemoryContext TopMemoryContext
Definition: mcxt.c:149
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
static uint32 pg_nextpower2_32(uint32 num)
Definition: pg_bitutils.h:189
#define lfirst(lc)
Definition: pg_list.h:172
static char ** options
StringInfo makeStringInfo(void)
Definition: stringinfo.c:72
char * defname
Definition: parsenodes.h:826
ParseLoc location
Definition: parsenodes.h:830
Node * arg
Definition: parsenodes.h:827
ExplainOptionHandler option_handler
Definition: explain_state.c:46
const char * option_name
Definition: explain_state.c:45
int extension_state_allocated
Definition: explain_state.h:76
StringInfo str
Definition: explain_state.h:46
void ** extension_state
Definition: explain_state.h:75
ExplainFormat format
Definition: explain_state.h:59
ExplainSerializeOption serialize
Definition: explain_state.h:58
Definition: pg_list.h:54