PostgreSQL Source Code: src/backend/commands/explain_state.c Source File

PostgreSQL Source Code git master
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"
38#include "commands/explain_state.h"
39
40/* Hook to perform additional EXPLAIN options validation */
41 explain_validate_options_hook_type explain_validate_options_hook = NULL;
42
43 typedef struct
44{
45 const char *option_name;
46 ExplainOptionHandler option_handler;
47} ExplainExtensionOption;
48
49 static const char **ExplainExtensionNameArray = NULL;
50 static int ExplainExtensionNamesAssigned = 0;
51 static int ExplainExtensionNamesAllocated = 0;
52
53 static ExplainExtensionOption *ExplainExtensionOptionArray = NULL;
54 static int ExplainExtensionOptionsAssigned = 0;
55 static int ExplainExtensionOptionsAllocated = 0;
56
57/*
58 * Create a new ExplainState struct initialized with default options.
59 */
60ExplainState *
61 NewExplainState(void)
62{
63 ExplainState *es = (ExplainState *) palloc0(sizeof(ExplainState));
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
77 ParseExplainOptionList(ExplainState *es, List *options, ParseState *pstate)
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)
125 es->serialize = EXPLAIN_SERIALIZE_NONE;
126 else if (strcmp(p, "text") == 0)
127 es->serialize = EXPLAIN_SERIALIZE_TEXT;
128 else if (strcmp(p, "binary") == 0)
129 es->serialize = EXPLAIN_SERIALIZE_BINARY;
130 else
131 ereport(ERROR,
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' */
140 es->serialize = EXPLAIN_SERIALIZE_TEXT;
141 }
142 }
143 else if (strcmp(opt->defname, "format") == 0)
144 {
145 char *p = defGetString(opt);
146
147 if (strcmp(p, "text") == 0)
148 es->format = EXPLAIN_FORMAT_TEXT;
149 else if (strcmp(p, "xml") == 0)
150 es->format = EXPLAIN_FORMAT_XML;
151 else if (strcmp(p, "json") == 0)
152 es->format = EXPLAIN_FORMAT_JSON;
153 else if (strcmp(p, "yaml") == 0)
154 es->format = EXPLAIN_FORMAT_YAML;
155 else
156 ereport(ERROR,
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))
163 ereport(ERROR,
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)
172 ereport(ERROR,
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)
184 ereport(ERROR,
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)
190 ereport(ERROR,
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)
196 ereport(ERROR,
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 */
204 if (explain_validate_options_hook)
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
220 GetExplainExtensionId(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 {
230 ExplainExtensionNamesAllocated = 16;
231 ExplainExtensionNameArray = (const char **)
232 MemoryContextAlloc(TopMemoryContext,
233 ExplainExtensionNamesAllocated
234 * sizeof(char *));
235 }
236
237 /* If there's an array but it's currently full, expand it. */
238 if (ExplainExtensionNamesAssigned >= ExplainExtensionNamesAllocated)
239 {
240 int i = pg_nextpower2_32(ExplainExtensionNamesAssigned + 1);
241
242 ExplainExtensionNameArray = (const char **)
243 repalloc(ExplainExtensionNameArray, i * sizeof(char *));
244 ExplainExtensionNamesAllocated = i;
245 }
246
247 /* Assign and return new ID. */
248 ExplainExtensionNameArray[ExplainExtensionNamesAssigned] = extension_name;
249 return ExplainExtensionNamesAssigned++;
250}
251
252/*
253 * Get extension-specific state from an ExplainState.
254 *
255 * See comments for SetExplainExtensionState, below.
256 */
257void *
258 GetExplainExtensionState(ExplainState *es, int extension_id)
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
277 SetExplainExtensionState(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 {
284 es->extension_state_allocated =
285 Max(16, pg_nextpower2_32(extension_id + 1));
286 es->extension_state =
287 palloc0(es->extension_state_allocated * sizeof(void *));
288 }
289
290 /* If there's an array but it's currently full, expand it. */
291 if (extension_id >= es->extension_state_allocated)
292 {
293 int i;
294
295 i = pg_nextpower2_32(extension_id + 1);
296 es->extension_state = (void **)
297 repalloc0(es->extension_state,
298 es->extension_state_allocated * sizeof(void *),
299 i * sizeof(void *));
300 es->extension_state_allocated = i;
301 }
302
303 es->extension_state[extension_id] = opaque;
304}
305
306/*
307 * Register a new EXPLAIN option.
308 *
309 * When option_name is used as an EXPLAIN option, handler will be called and
310 * should update the ExplainState passed to it. See comments at top of file
311 * for a more detailed explanation.
312 *
313 * option_name is assumed to be a constant string or allocated in storage
314 * that will never be freed.
315 */
316void
317 RegisterExtensionExplainOption(const char *option_name,
318 ExplainOptionHandler handler)
319{
320 ExplainExtensionOption *exopt;
321
322 /* Search for an existing option by this name; if found, update handler. */
323 for (int i = 0; i < ExplainExtensionOptionsAssigned; ++i)
324 {
325 if (strcmp(ExplainExtensionOptionArray[i].option_name,
326 option_name) == 0)
327 {
328 ExplainExtensionOptionArray[i].option_handler = handler;
329 return;
330 }
331 }
332
333 /* If there is no array yet, create one. */
334 if (ExplainExtensionOptionArray == NULL)
335 {
336 ExplainExtensionOptionsAllocated = 16;
337 ExplainExtensionOptionArray = (ExplainExtensionOption *)
338 MemoryContextAlloc(TopMemoryContext,
339 ExplainExtensionOptionsAllocated
340 * sizeof(char *));
341 }
342
343 /* If there's an array but it's currently full, expand it. */
344 if (ExplainExtensionOptionsAssigned >= ExplainExtensionOptionsAllocated)
345 {
346 int i = pg_nextpower2_32(ExplainExtensionOptionsAssigned + 1);
347
348 ExplainExtensionOptionArray = (ExplainExtensionOption *)
349 repalloc(ExplainExtensionOptionArray, i * sizeof(char *));
350 ExplainExtensionOptionsAllocated = i;
351 }
352
353 /* Assign and return new ID. */
354 exopt = &ExplainExtensionOptionArray[ExplainExtensionOptionsAssigned++];
355 exopt->option_name = option_name;
356 exopt->option_handler = handler;
357}
358
359/*
360 * Apply an EXPLAIN option registered by an extension.
361 *
362 * If no extension has registered the named option, returns false. Otherwise,
363 * calls the appropriate handler function and then returns true.
364 */
365bool
366 ApplyExtensionExplainOption(ExplainState *es, DefElem *opt, ParseState *pstate)
367{
368 for (int i = 0; i < ExplainExtensionOptionsAssigned; ++i)
369 {
370 if (strcmp(ExplainExtensionOptionArray[i].option_name,
371 opt->defname) == 0)
372 {
373 ExplainExtensionOptionArray[i].option_handler(es, opt, pstate);
374 return true;
375 }
376 }
377
378 return false;
379}
#define Max(x, y)
Definition: c.h:997
char * defGetString(DefElem *def)
Definition: define.c:35
bool defGetBoolean(DefElem *def)
Definition: define.c:94
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:150
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)
Definition: explain_state.c:220
void * GetExplainExtensionState(ExplainState *es, int extension_id)
Definition: explain_state.c:258
static int ExplainExtensionNamesAllocated
Definition: explain_state.c:51
void SetExplainExtensionState(ExplainState *es, int extension_id, void *opaque)
Definition: explain_state.c:277
void RegisterExtensionExplainOption(const char *option_name, ExplainOptionHandler handler)
Definition: explain_state.c:317
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)
Definition: explain_state.c:366
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
@ 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
void(* explain_validate_options_hook_type)(ExplainState *es, List *options, ParseState *pstate)
Definition: explain_state.h:82
Assert(PointerIsAligned(start, uint64))
i
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:1682
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1229
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1610
void * palloc0(Size size)
Definition: mcxt.c:1395
MemoryContext TopMemoryContext
Definition: mcxt.c:166
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
Definition: pg_recvlogical.c:59
StringInfo makeStringInfo(void)
Definition: stringinfo.c:72
char * defname
Definition: parsenodes.h:843
ParseLoc location
Definition: parsenodes.h:847
Node * arg
Definition: parsenodes.h:844
ExplainOptionHandler option_handler
Definition: explain_state.c:46
const char * option_name
Definition: explain_state.c:45
bool verbose
Definition: explain_state.h:48
bool settings
Definition: explain_state.h:56
int extension_state_allocated
Definition: explain_state.h:76
bool analyze
Definition: explain_state.h:49
StringInfo str
Definition: explain_state.h:46
void ** extension_state
Definition: explain_state.h:75
ExplainFormat format
Definition: explain_state.h:59
bool generic
Definition: explain_state.h:57
bool summary
Definition: explain_state.h:54
ExplainSerializeOption serialize
Definition: explain_state.h:58
bool buffers
Definition: explain_state.h:51
Definition: pg_list.h:54
Definition: oid2name.c:30
Definition: pg_list.h:46

AltStyle によって変換されたページ (->オリジナル) /