Age Owner Branch data TLA Line data Source code
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"
38 : : #include "commands/explain_state.h"
39 : : #include "utils/builtins.h"
40 : : #include "utils/guc.h"
41 : :
42 : : /* Hook to perform additional EXPLAIN options validation */
43 : : explain_validate_options_hook_type explain_validate_options_hook = NULL;
44 : :
45 : : typedef struct
46 : : {
47 : : const char *option_name;
48 : : ExplainOptionHandler option_handler;
49 : : ExplainOptionGUCCheckHandler guc_check_handler;
50 : : } ExplainExtensionOption;
51 : :
52 : : static const char **ExplainExtensionNameArray = NULL;
53 : : static int ExplainExtensionNamesAssigned = 0;
54 : : static int ExplainExtensionNamesAllocated = 0;
55 : :
56 : : static ExplainExtensionOption *ExplainExtensionOptionArray = NULL;
57 : : static int ExplainExtensionOptionsAssigned = 0;
58 : : static int ExplainExtensionOptionsAllocated = 0;
59 : :
60 : : /*
61 : : * Create a new ExplainState struct initialized with default options.
62 : : */
63 : : ExplainState *
527 rhaas@postgresql.org 64 :CBC 16886 : NewExplainState(void)
65 : : {
260 michael@paquier.xyz 66 : 16886 : ExplainState *es = palloc0_object(ExplainState);
67 : :
68 : : /* Set default options (most fields can be left as zeroes). */
527 rhaas@postgresql.org 69 : 16886 : es->costs = true;
70 : : /* Prepare output buffer. */
71 : 16886 : es->str = makeStringInfo();
72 : :
73 : 16886 : return es;
74 : : }
75 : :
76 : : /*
77 : : * Parse a list of EXPLAIN options and update an ExplainState accordingly.
78 : : */
79 : : void
80 : 16875 : ParseExplainOptionList(ExplainState *es, List *options, ParseState *pstate)
81 : : {
82 : : ListCell *lc;
83 : 16875 : bool timing_set = false;
84 : 16875 : bool buffers_set = false;
85 : 16875 : bool summary_set = false;
86 : :
87 : : /* Parse options list. */
88 [ + + + + : 33637 : foreach(lc, options)
+ + ]
89 : : {
90 : 16766 : DefElem *opt = (DefElem *) lfirst(lc);
91 : :
92 [ + + ]: 16766 : if (strcmp(opt->defname, "analyze") == 0)
93 : 2362 : es->analyze = defGetBoolean(opt);
94 [ + + ]: 14404 : else if (strcmp(opt->defname, "verbose") == 0)
95 : 1833 : es->verbose = defGetBoolean(opt);
96 [ + + ]: 12571 : else if (strcmp(opt->defname, "costs") == 0)
97 : 10297 : es->costs = defGetBoolean(opt);
98 [ + + ]: 2274 : else if (strcmp(opt->defname, "buffers") == 0)
99 : : {
100 : 650 : buffers_set = true;
101 : 650 : es->buffers = defGetBoolean(opt);
102 : : }
103 [ - + ]: 1624 : else if (strcmp(opt->defname, "wal") == 0)
527 rhaas@postgresql.org 104 :UBC 0 : es->wal = defGetBoolean(opt);
527 rhaas@postgresql.org 105 [ + + ]:CBC 1624 : else if (strcmp(opt->defname, "settings") == 0)
106 : 8 : es->settings = defGetBoolean(opt);
107 [ + + ]: 1616 : else if (strcmp(opt->defname, "generic_plan") == 0)
108 : 12 : es->generic = defGetBoolean(opt);
109 [ + + ]: 1604 : else if (strcmp(opt->defname, "timing") == 0)
110 : : {
111 : 606 : timing_set = true;
112 : 606 : es->timing = defGetBoolean(opt);
113 : : }
114 [ + + ]: 998 : else if (strcmp(opt->defname, "summary") == 0)
115 : : {
116 : 590 : summary_set = true;
117 : 590 : es->summary = defGetBoolean(opt);
118 : : }
119 [ + + ]: 408 : else if (strcmp(opt->defname, "memory") == 0)
120 : 20 : es->memory = defGetBoolean(opt);
121 [ + + ]: 388 : else if (strcmp(opt->defname, "serialize") == 0)
122 : : {
123 [ + + ]: 20 : if (opt->arg)
124 : : {
125 : 8 : char *p = defGetString(opt);
126 : :
127 [ + - - + ]: 8 : if (strcmp(p, "off") == 0 || strcmp(p, "none") == 0)
527 rhaas@postgresql.org 128 :UBC 0 : es->serialize = EXPLAIN_SERIALIZE_NONE;
527 rhaas@postgresql.org 129 [ + + ]:CBC 8 : else if (strcmp(p, "text") == 0)
130 : 4 : es->serialize = EXPLAIN_SERIALIZE_TEXT;
131 [ + - ]: 4 : else if (strcmp(p, "binary") == 0)
132 : 4 : es->serialize = EXPLAIN_SERIALIZE_BINARY;
133 : : else
527 rhaas@postgresql.org 134 [ # # ]:UBC 0 : ereport(ERROR,
135 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
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' */
527 rhaas@postgresql.org 143 :CBC 12 : es->serialize = EXPLAIN_SERIALIZE_TEXT;
144 : : }
145 : : }
146 [ + + ]: 368 : else if (strcmp(opt->defname, "format") == 0)
147 : : {
148 : 204 : char *p = defGetString(opt);
149 : :
150 [ + + ]: 204 : if (strcmp(p, "text") == 0)
151 : 9 : es->format = EXPLAIN_FORMAT_TEXT;
152 [ + + ]: 195 : else if (strcmp(p, "xml") == 0)
153 : 5 : es->format = EXPLAIN_FORMAT_XML;
154 [ + + ]: 190 : else if (strcmp(p, "json") == 0)
155 : 182 : es->format = EXPLAIN_FORMAT_JSON;
156 [ + - ]: 8 : else if (strcmp(p, "yaml") == 0)
157 : 8 : es->format = EXPLAIN_FORMAT_YAML;
158 : : else
527 rhaas@postgresql.org 159 [ # # ]:UBC 0 : ereport(ERROR,
160 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
161 : : errmsg("unrecognized value for %s option \"%s\": \"%s\"",
162 : : "EXPLAIN", opt->defname, p),
163 : : parser_errposition(pstate, opt->location)));
164 : : }
142 tomas.vondra@postgre 165 [ + + ]:CBC 164 : else if (strcmp(opt->defname, "io") == 0)
166 : 8 : es->io = defGetBoolean(opt);
527 rhaas@postgresql.org 167 [ + + ]: 156 : else if (!ApplyExtensionExplainOption(es, opt, pstate))
168 [ + - ]: 4 : ereport(ERROR,
169 : : (errcode(ERRCODE_SYNTAX_ERROR),
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 [ - + - - ]: 16871 : if (es->wal && !es->analyze)
527 rhaas@postgresql.org 177 [ # # ]:UBC 0 : ereport(ERROR,
178 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
179 : : errmsg("EXPLAIN option %s requires ANALYZE", "WAL")));
180 : :
181 : : /* if the timing was not set explicitly, set default value */
527 rhaas@postgresql.org 182 [ + + ]:CBC 16871 : es->timing = (timing_set) ? es->timing : es->analyze;
183 : :
184 : : /* if the buffers was not set explicitly, set default value */
185 [ + + ]: 16871 : es->buffers = (buffers_set) ? es->buffers : es->analyze;
186 : :
187 : : /* check that timing is used with EXPLAIN ANALYZE */
188 [ + + - + ]: 16871 : if (es->timing && !es->analyze)
527 rhaas@postgresql.org 189 [ # # ]:UBC 0 : ereport(ERROR,
190 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
191 : : errmsg("EXPLAIN option %s requires ANALYZE", "TIMING")));
192 : :
193 : : /* check that IO is used with EXPLAIN ANALYZE */
142 tomas.vondra@postgre 194 [ + + - + ]:CBC 16871 : if (es->io && !es->analyze)
142 tomas.vondra@postgre 195 [ # # ]:UBC 0 : ereport(ERROR,
196 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
197 : : errmsg("EXPLAIN option %s requires ANALYZE", "IO")));
198 : :
199 : : /* check that serialize is used with EXPLAIN ANALYZE */
527 rhaas@postgresql.org 200 [ + + - + ]:CBC 16871 : if (es->serialize != EXPLAIN_SERIALIZE_NONE && !es->analyze)
527 rhaas@postgresql.org 201 [ # # ]:UBC 0 : ereport(ERROR,
202 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
203 : : errmsg("EXPLAIN option %s requires ANALYZE", "SERIALIZE")));
204 : :
205 : : /* check that GENERIC_PLAN is not used with EXPLAIN ANALYZE */
527 rhaas@postgresql.org 206 [ + + + + ]:CBC 16871 : if (es->generic && es->analyze)
207 [ + - ]: 4 : ereport(ERROR,
208 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
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 [ + + ]: 16867 : es->summary = (summary_set) ? es->summary : es->analyze;
214 : :
215 : : /* plugin specific option validation */
525 216 [ - + ]: 16867 : if (explain_validate_options_hook)
525 rhaas@postgresql.org 217 :UBC 0 : (*explain_validate_options_hook) (es, options, pstate);
527 rhaas@postgresql.org 218 :CBC 16867 : }
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 : : */
231 : : int
232 : 37 : GetExplainExtensionId(const char *extension_name)
233 : : {
234 : : /* Search for an existing extension by this name; if found, return ID. */
235 [ - + ]: 37 : for (int i = 0; i < ExplainExtensionNamesAssigned; ++i)
527 rhaas@postgresql.org 236 [ # # ]:UBC 0 : if (strcmp(ExplainExtensionNameArray[i], extension_name) == 0)
237 : 0 : return i;
238 : :
239 : : /* If there is no array yet, create one. */
527 rhaas@postgresql.org 240 [ + - ]:CBC 37 : if (ExplainExtensionNameArray == NULL)
241 : : {
242 : 37 : ExplainExtensionNamesAllocated = 16;
243 : 37 : ExplainExtensionNameArray = (const char **)
244 : 37 : MemoryContextAlloc(TopMemoryContext,
245 : : ExplainExtensionNamesAllocated
246 : : * sizeof(char *));
247 : : }
248 : :
249 : : /* If there's an array but it's currently full, expand it. */
250 [ - + ]: 37 : if (ExplainExtensionNamesAssigned >= ExplainExtensionNamesAllocated)
251 : : {
527 rhaas@postgresql.org 252 :UBC 0 : int i = pg_nextpower2_32(ExplainExtensionNamesAssigned + 1);
253 : :
10 michael@paquier.xyz 254 :UNC 0 : ExplainExtensionNameArray = repalloc_array(ExplainExtensionNameArray, const char *, i);
527 rhaas@postgresql.org 255 :UBC 0 : ExplainExtensionNamesAllocated = i;
256 : : }
257 : :
258 : : /* Assign and return new ID. */
527 rhaas@postgresql.org 259 :CBC 37 : ExplainExtensionNameArray[ExplainExtensionNamesAssigned] = extension_name;
260 : 37 : return ExplainExtensionNamesAssigned++;
261 : : }
262 : :
263 : : /*
264 : : * Get extension-specific state from an ExplainState.
265 : : *
266 : : * See comments for SetExplainExtensionState, below.
267 : : */
268 : : void *
269 : 7845 : GetExplainExtensionState(ExplainState *es, int extension_id)
270 : : {
271 [ - + ]: 7845 : Assert(extension_id >= 0);
272 : :
273 [ + + ]: 7845 : if (extension_id >= es->extension_state_allocated)
274 : 7507 : return NULL;
275 : :
276 : 338 : return es->extension_state[extension_id];
277 : : }
278 : :
279 : : /*
280 : : * Store extension-specific state into an ExplainState.
281 : : *
282 : : * To use this function, first obtain an integer extension_id using
283 : : * GetExplainExtensionId. Then use this function to store an opaque pointer
284 : : * in the ExplainState. Later, you can retrieve the opaque pointer using
285 : : * GetExplainExtensionState.
286 : : */
287 : : void
288 : 151 : SetExplainExtensionState(ExplainState *es, int extension_id, void *opaque)
289 : : {
290 [ - + ]: 151 : Assert(extension_id >= 0);
291 : :
292 : : /* If there is no array yet, create one. */
293 [ + - ]: 151 : if (es->extension_state == NULL)
294 : : {
336 295 : 151 : es->extension_state_allocated =
296 [ - + ]: 151 : Max(16, pg_nextpower2_32(extension_id + 1));
10 michael@paquier.xyz 297 :GNC 151 : es->extension_state = palloc0_array(void *, es->extension_state_allocated);
298 : : }
299 : :
300 : : /* If there's an array but it's currently full, expand it. */
527 rhaas@postgresql.org 301 [ - + ]:CBC 151 : if (extension_id >= es->extension_state_allocated)
302 : : {
303 : : int i;
304 : :
336 rhaas@postgresql.org 305 :UBC 0 : i = pg_nextpower2_32(extension_id + 1);
260 michael@paquier.xyz 306 : 0 : es->extension_state = repalloc0_array(es->extension_state, void *, es->extension_state_allocated, i);
527 rhaas@postgresql.org 307 : 0 : es->extension_state_allocated = i;
308 : : }
309 : :
527 rhaas@postgresql.org 310 :CBC 151 : es->extension_state[extension_id] = opaque;
311 : 151 : }
312 : :
313 : : /*
314 : : * Register a new EXPLAIN option.
315 : : *
316 : : * option_name is assumed to be a constant string or allocated in storage
317 : : * that will never be freed.
318 : : *
319 : : * When option_name is used as an EXPLAIN option, handler will be called and
320 : : * should update the ExplainState passed to it. See comments at top of file
321 : : * for a more detailed explanation.
322 : : *
323 : : * guc_check_handler is a function that can be safely called from a
324 : : * GUC check hook to validate a proposed value for a custom EXPLAIN option.
325 : : * Boolean-valued options can pass GUCCheckBooleanExplainOption. See the
326 : : * comments for GUCCheckBooleanExplainOption for further information on
327 : : * how a guc_check_handler should behave.
328 : : */
329 : : void
330 : 52 : RegisterExtensionExplainOption(const char *option_name,
331 : : ExplainOptionHandler handler,
332 : : ExplainOptionGUCCheckHandler guc_check_handler)
333 : : {
334 : : ExplainExtensionOption *exopt;
335 : :
143 336 [ - + ]: 52 : Assert(handler != NULL);
337 [ - + ]: 52 : Assert(guc_check_handler != NULL);
338 : :
339 : : /* Search for an existing option by this name; if found, update handler. */
527 340 [ + + ]: 67 : for (int i = 0; i < ExplainExtensionOptionsAssigned; ++i)
341 : : {
342 [ - + ]: 15 : if (strcmp(ExplainExtensionOptionArray[i].option_name,
343 : : option_name) == 0)
344 : : {
143 rhaas@postgresql.org 345 :UBC 0 : exopt = &ExplainExtensionOptionArray[i];
346 : :
347 : 0 : exopt->option_handler = handler;
348 : 0 : exopt->guc_check_handler = guc_check_handler;
527 349 : 0 : return;
350 : : }
351 : : }
352 : :
353 : : /* If there is no array yet, create one. */
527 rhaas@postgresql.org 354 [ + + ]:CBC 52 : if (ExplainExtensionOptionArray == NULL)
355 : : {
356 : 37 : ExplainExtensionOptionsAllocated = 16;
357 : 37 : ExplainExtensionOptionArray = (ExplainExtensionOption *)
358 : 37 : MemoryContextAlloc(TopMemoryContext,
359 : : ExplainExtensionOptionsAllocated
360 : : * sizeof(ExplainExtensionOption));
361 : : }
362 : :
363 : : /* If there's an array but it's currently full, expand it. */
364 [ - + ]: 52 : if (ExplainExtensionOptionsAssigned >= ExplainExtensionOptionsAllocated)
365 : : {
527 rhaas@postgresql.org 366 :UBC 0 : int i = pg_nextpower2_32(ExplainExtensionOptionsAssigned + 1);
367 : :
10 michael@paquier.xyz 368 :UNC 0 : ExplainExtensionOptionArray = repalloc_array(ExplainExtensionOptionArray,
369 : : ExplainExtensionOption, i);
527 rhaas@postgresql.org 370 :UBC 0 : ExplainExtensionOptionsAllocated = i;
371 : : }
372 : :
373 : : /* Assign and return new ID. */
527 rhaas@postgresql.org 374 :CBC 52 : exopt = &ExplainExtensionOptionArray[ExplainExtensionOptionsAssigned++];
375 : 52 : exopt->option_name = option_name;
376 : 52 : exopt->option_handler = handler;
143 377 : 52 : exopt->guc_check_handler = guc_check_handler;
378 : : }
379 : :
380 : : /*
381 : : * Apply an EXPLAIN option registered by an extension.
382 : : *
383 : : * If no extension has registered the named option, returns false. Otherwise,
384 : : * calls the appropriate handler function and then returns true.
385 : : */
386 : : bool
527 387 : 157 : ApplyExtensionExplainOption(ExplainState *es, DefElem *opt, ParseState *pstate)
388 : : {
389 [ + + ]: 169 : for (int i = 0; i < ExplainExtensionOptionsAssigned; ++i)
390 : : {
391 : 165 : if (strcmp(ExplainExtensionOptionArray[i].option_name,
392 [ + + ]: 165 : opt->defname) == 0)
393 : : {
394 : 153 : ExplainExtensionOptionArray[i].option_handler(es, opt, pstate);
395 : 153 : return true;
396 : : }
397 : : }
398 : :
399 : 4 : return false;
400 : : }
401 : :
402 : : /*
403 : : * Determine whether an EXPLAIN extension option will be accepted without
404 : : * error. Returns true if so, and false if not. See the comments for
405 : : * GUCCheckBooleanExplainOption for more details.
406 : : *
407 : : * The caller need not know that the option_name is valid; this function
408 : : * will indicate that the option is unrecognized if that is the case.
409 : : */
410 : : bool
143 411 : 23 : GUCCheckExplainExtensionOption(const char *option_name,
412 : : const char *option_value,
413 : : NodeTag option_type)
414 : : {
415 [ + + ]: 32 : for (int i = 0; i < ExplainExtensionOptionsAssigned; i++)
416 : : {
417 : 31 : ExplainExtensionOption *exopt = &ExplainExtensionOptionArray[i];
418 : :
419 [ + + ]: 31 : if (strcmp(exopt->option_name, option_name) == 0)
420 : 22 : return exopt->guc_check_handler(option_name, option_value,
421 : : option_type);
422 : : }
423 : :
424 : : /* Unrecognized option name. */
7 peter@eisentraut.org 425 : 1 : GUC_check_errmsg("unrecognized %s option \"%s\"", "EXPLAIN", option_name);
143 rhaas@postgresql.org 426 : 1 : return false;
427 : : }
428 : :
429 : : /*
430 : : * guc_check_handler for Boolean-valued EXPLAIN extension options.
431 : : *
432 : : * After receiving a "true" value from this or any other GUC check handler
433 : : * for an EXPLAIN extension option, the caller is entitled to assume that
434 : : * a suitably constructed DefElem passed to the main option handler will
435 : : * not cause an error. To construct this DefElem, the caller should set
436 : : * the DefElem's defname to option_name. If option_value is NULL, arg
437 : : * should be NULL. Otherwise, arg should be of the type given by
438 : : * option_type, with option_value as the associated value. The only option
439 : : * types that should be passed are T_String, T_Float, and T_Integer; in
440 : : * the last case, the caller will need to perform a string-to-integer
441 : : * conversion.
442 : : *
443 : : * A guc_check_handler should not throw an error, and should not allocate
444 : : * memory. If it returns false to indicate that the option_value is not
445 : : * acceptable, it may use GUC_check_errmsg(), GUC_check_errdetail(), etc.
446 : : * to clarify the nature of the problem.
447 : : *
448 : : * Since we're concerned with Boolean options here, the logic below must
449 : : * exactly match the semantics of defGetBoolean.
450 : : */
451 : : bool
452 : 22 : GUCCheckBooleanExplainOption(const char *option_name,
453 : : const char *option_value,
454 : : NodeTag option_type)
455 : : {
456 : 22 : bool valid = false;
457 : :
458 [ + + ]: 22 : if (option_value == NULL)
459 : : {
460 : : /* defGetBoolean treats no argument as valid */
461 : 12 : valid = true;
462 : : }
463 [ + + ]: 10 : else if (option_type == T_String)
464 : : {
465 : : /* defGetBoolean accepts exactly these string values */
466 [ + + + + ]: 12 : if (pg_strcasecmp(option_value, "true") == 0 ||
467 [ + - ]: 8 : pg_strcasecmp(option_value, "false") == 0 ||
468 [ + + ]: 6 : pg_strcasecmp(option_value, "on") == 0 ||
469 : 3 : pg_strcasecmp(option_value, "off") == 0)
470 : 5 : valid = true;
471 : : }
472 [ + + ]: 3 : else if (option_type == T_Integer)
473 : : {
474 : : long value;
475 : : char *end;
476 : :
477 : : /*
478 : : * defGetBoolean accepts only 0 and 1, but those can be spelled in
479 : : * various ways (e.g. 01, 0x01).
480 : : */
481 : 2 : errno = 0;
482 : 2 : value = strtol(option_value, &end, 0);
483 [ + - + - : 2 : if (errno == 0 && *end == '\0' && end != option_value &&
+ - ]
484 [ + - + - : 2 : value == (int) value && (value == 0 || value == 1))
+ + ]
485 : 1 : valid = true;
486 : : }
487 : :
488 [ + + ]: 22 : if (!valid)
489 : : {
7 peter@eisentraut.org 490 : 4 : GUC_check_errmsg("%s option \"%s\" requires a Boolean value",
491 : : "EXPLAIN", option_name);
143 rhaas@postgresql.org 492 : 4 : return false;
493 : : }
494 : :
495 : 18 : return true;
496 : : }
|