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

PostgreSQL Source Code git master
explain_format.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * explain_format.c
4 * Format routines for explaining query execution plans
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 * IDENTIFICATION
10 * src/backend/commands/explain_format.c
11 *
12 *-------------------------------------------------------------------------
13 */
14#include "postgres.h"
15
16#include "commands/explain.h"
17#include "commands/explain_format.h"
18#include "commands/explain_state.h"
19#include "utils/json.h"
20#include "utils/xml.h"
21
22/* OR-able flags for ExplainXMLTag() */
23 #define X_OPENING 0
24 #define X_CLOSING 1
25 #define X_CLOSE_IMMEDIATE 2
26 #define X_NOWHITESPACE 4
27
28static void ExplainJSONLineEnding(ExplainState *es);
29static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
30static void ExplainYAMLLineStarting(ExplainState *es);
31static void escape_yaml(StringInfo buf, const char *str);
32
33/*
34 * Explain a property, such as sort keys or targets, that takes the form of
35 * a list of unlabeled items. "data" is a list of C strings.
36 */
37void
38 ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
39{
40 ListCell *lc;
41 bool first = true;
42
43 switch (es->format)
44 {
45 case EXPLAIN_FORMAT_TEXT:
46 ExplainIndentText(es);
47 appendStringInfo(es->str, "%s: ", qlabel);
48 foreach(lc, data)
49 {
50 if (!first)
51 appendStringInfoString(es->str, ", ");
52 appendStringInfoString(es->str, (const char *) lfirst(lc));
53 first = false;
54 }
55 appendStringInfoChar(es->str, '\n');
56 break;
57
58 case EXPLAIN_FORMAT_XML:
59 ExplainXMLTag(qlabel, X_OPENING, es);
60 foreach(lc, data)
61 {
62 char *str;
63
64 appendStringInfoSpaces(es->str, es->indent * 2 + 2);
65 appendStringInfoString(es->str, "<Item>");
66 str = escape_xml((const char *) lfirst(lc));
67 appendStringInfoString(es->str, str);
68 pfree(str);
69 appendStringInfoString(es->str, "</Item>\n");
70 }
71 ExplainXMLTag(qlabel, X_CLOSING, es);
72 break;
73
74 case EXPLAIN_FORMAT_JSON:
75 ExplainJSONLineEnding(es);
76 appendStringInfoSpaces(es->str, es->indent * 2);
77 escape_json(es->str, qlabel);
78 appendStringInfoString(es->str, ": [");
79 foreach(lc, data)
80 {
81 if (!first)
82 appendStringInfoString(es->str, ", ");
83 escape_json(es->str, (const char *) lfirst(lc));
84 first = false;
85 }
86 appendStringInfoChar(es->str, ']');
87 break;
88
89 case EXPLAIN_FORMAT_YAML:
90 ExplainYAMLLineStarting(es);
91 appendStringInfo(es->str, "%s: ", qlabel);
92 foreach(lc, data)
93 {
94 appendStringInfoChar(es->str, '\n');
95 appendStringInfoSpaces(es->str, es->indent * 2 + 2);
96 appendStringInfoString(es->str, "- ");
97 escape_yaml(es->str, (const char *) lfirst(lc));
98 }
99 break;
100 }
101}
102
103/*
104 * Explain a property that takes the form of a list of unlabeled items within
105 * another list. "data" is a list of C strings.
106 */
107void
108 ExplainPropertyListNested(const char *qlabel, List *data, ExplainState *es)
109{
110 ListCell *lc;
111 bool first = true;
112
113 switch (es->format)
114 {
115 case EXPLAIN_FORMAT_TEXT:
116 case EXPLAIN_FORMAT_XML:
117 ExplainPropertyList(qlabel, data, es);
118 return;
119
120 case EXPLAIN_FORMAT_JSON:
121 ExplainJSONLineEnding(es);
122 appendStringInfoSpaces(es->str, es->indent * 2);
123 appendStringInfoChar(es->str, '[');
124 foreach(lc, data)
125 {
126 if (!first)
127 appendStringInfoString(es->str, ", ");
128 escape_json(es->str, (const char *) lfirst(lc));
129 first = false;
130 }
131 appendStringInfoChar(es->str, ']');
132 break;
133
134 case EXPLAIN_FORMAT_YAML:
135 ExplainYAMLLineStarting(es);
136 appendStringInfoString(es->str, "- [");
137 foreach(lc, data)
138 {
139 if (!first)
140 appendStringInfoString(es->str, ", ");
141 escape_yaml(es->str, (const char *) lfirst(lc));
142 first = false;
143 }
144 appendStringInfoChar(es->str, ']');
145 break;
146 }
147}
148
149/*
150 * Explain a simple property.
151 *
152 * If "numeric" is true, the value is a number (or other value that
153 * doesn't need quoting in JSON).
154 *
155 * If unit is non-NULL the text format will display it after the value.
156 *
157 * This usually should not be invoked directly, but via one of the datatype
158 * specific routines ExplainPropertyText, ExplainPropertyInteger, etc.
159 */
160static void
161 ExplainProperty(const char *qlabel, const char *unit, const char *value,
162 bool numeric, ExplainState *es)
163{
164 switch (es->format)
165 {
166 case EXPLAIN_FORMAT_TEXT:
167 ExplainIndentText(es);
168 if (unit)
169 appendStringInfo(es->str, "%s: %s %s\n", qlabel, value, unit);
170 else
171 appendStringInfo(es->str, "%s: %s\n", qlabel, value);
172 break;
173
174 case EXPLAIN_FORMAT_XML:
175 {
176 char *str;
177
178 appendStringInfoSpaces(es->str, es->indent * 2);
179 ExplainXMLTag(qlabel, X_OPENING | X_NOWHITESPACE, es);
180 str = escape_xml(value);
181 appendStringInfoString(es->str, str);
182 pfree(str);
183 ExplainXMLTag(qlabel, X_CLOSING | X_NOWHITESPACE, es);
184 appendStringInfoChar(es->str, '\n');
185 }
186 break;
187
188 case EXPLAIN_FORMAT_JSON:
189 ExplainJSONLineEnding(es);
190 appendStringInfoSpaces(es->str, es->indent * 2);
191 escape_json(es->str, qlabel);
192 appendStringInfoString(es->str, ": ");
193 if (numeric)
194 appendStringInfoString(es->str, value);
195 else
196 escape_json(es->str, value);
197 break;
198
199 case EXPLAIN_FORMAT_YAML:
200 ExplainYAMLLineStarting(es);
201 appendStringInfo(es->str, "%s: ", qlabel);
202 if (numeric)
203 appendStringInfoString(es->str, value);
204 else
205 escape_yaml(es->str, value);
206 break;
207 }
208}
209
210/*
211 * Explain a string-valued property.
212 */
213void
214 ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
215{
216 ExplainProperty(qlabel, NULL, value, false, es);
217}
218
219/*
220 * Explain an integer-valued property.
221 */
222void
223 ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value,
224 ExplainState *es)
225{
226 char buf[32];
227
228 snprintf(buf, sizeof(buf), INT64_FORMAT, value);
229 ExplainProperty(qlabel, unit, buf, true, es);
230}
231
232/*
233 * Explain an unsigned integer-valued property.
234 */
235void
236 ExplainPropertyUInteger(const char *qlabel, const char *unit, uint64 value,
237 ExplainState *es)
238{
239 char buf[32];
240
241 snprintf(buf, sizeof(buf), UINT64_FORMAT, value);
242 ExplainProperty(qlabel, unit, buf, true, es);
243}
244
245/*
246 * Explain a float-valued property, using the specified number of
247 * fractional digits.
248 */
249void
250 ExplainPropertyFloat(const char *qlabel, const char *unit, double value,
251 int ndigits, ExplainState *es)
252{
253 char *buf;
254
255 buf = psprintf("%.*f", ndigits, value);
256 ExplainProperty(qlabel, unit, buf, true, es);
257 pfree(buf);
258}
259
260/*
261 * Explain a bool-valued property.
262 */
263void
264 ExplainPropertyBool(const char *qlabel, bool value, ExplainState *es)
265{
266 ExplainProperty(qlabel, NULL, value ? "true" : "false", true, es);
267}
268
269/*
270 * Open a group of related objects.
271 *
272 * objtype is the type of the group object, labelname is its label within
273 * a containing object (if any).
274 *
275 * If labeled is true, the group members will be labeled properties,
276 * while if it's false, they'll be unlabeled objects.
277 */
278void
279 ExplainOpenGroup(const char *objtype, const char *labelname,
280 bool labeled, ExplainState *es)
281{
282 switch (es->format)
283 {
284 case EXPLAIN_FORMAT_TEXT:
285 /* nothing to do */
286 break;
287
288 case EXPLAIN_FORMAT_XML:
289 ExplainXMLTag(objtype, X_OPENING, es);
290 es->indent++;
291 break;
292
293 case EXPLAIN_FORMAT_JSON:
294 ExplainJSONLineEnding(es);
295 appendStringInfoSpaces(es->str, 2 * es->indent);
296 if (labelname)
297 {
298 escape_json(es->str, labelname);
299 appendStringInfoString(es->str, ": ");
300 }
301 appendStringInfoChar(es->str, labeled ? '{' : '[');
302
303 /*
304 * In JSON format, the grouping_stack is an integer list. 0 means
305 * we've emitted nothing at this grouping level, 1 means we've
306 * emitted something (and so the next item needs a comma). See
307 * ExplainJSONLineEnding().
308 */
309 es->grouping_stack = lcons_int(0, es->grouping_stack);
310 es->indent++;
311 break;
312
313 case EXPLAIN_FORMAT_YAML:
314
315 /*
316 * In YAML format, the grouping stack is an integer list. 0 means
317 * we've emitted nothing at this grouping level AND this grouping
318 * level is unlabeled and must be marked with "- ". See
319 * ExplainYAMLLineStarting().
320 */
321 ExplainYAMLLineStarting(es);
322 if (labelname)
323 {
324 appendStringInfo(es->str, "%s: ", labelname);
325 es->grouping_stack = lcons_int(1, es->grouping_stack);
326 }
327 else
328 {
329 appendStringInfoString(es->str, "- ");
330 es->grouping_stack = lcons_int(0, es->grouping_stack);
331 }
332 es->indent++;
333 break;
334 }
335}
336
337/*
338 * Close a group of related objects.
339 * Parameters must match the corresponding ExplainOpenGroup call.
340 */
341void
342 ExplainCloseGroup(const char *objtype, const char *labelname,
343 bool labeled, ExplainState *es)
344{
345 switch (es->format)
346 {
347 case EXPLAIN_FORMAT_TEXT:
348 /* nothing to do */
349 break;
350
351 case EXPLAIN_FORMAT_XML:
352 es->indent--;
353 ExplainXMLTag(objtype, X_CLOSING, es);
354 break;
355
356 case EXPLAIN_FORMAT_JSON:
357 es->indent--;
358 appendStringInfoChar(es->str, '\n');
359 appendStringInfoSpaces(es->str, 2 * es->indent);
360 appendStringInfoChar(es->str, labeled ? '}' : ']');
361 es->grouping_stack = list_delete_first(es->grouping_stack);
362 break;
363
364 case EXPLAIN_FORMAT_YAML:
365 es->indent--;
366 es->grouping_stack = list_delete_first(es->grouping_stack);
367 break;
368 }
369}
370
371/*
372 * Open a group of related objects, without emitting actual data.
373 *
374 * Prepare the formatting state as though we were beginning a group with
375 * the identified properties, but don't actually emit anything. Output
376 * subsequent to this call can be redirected into a separate output buffer,
377 * and then eventually appended to the main output buffer after doing a
378 * regular ExplainOpenGroup call (with the same parameters).
379 *
380 * The extra "depth" parameter is the new group's depth compared to current.
381 * It could be more than one, in case the eventual output will be enclosed
382 * in additional nesting group levels. We assume we don't need to track
383 * formatting state for those levels while preparing this group's output.
384 *
385 * There is no ExplainCloseSetAsideGroup --- in current usage, we always
386 * pop this state with ExplainSaveGroup.
387 */
388void
389 ExplainOpenSetAsideGroup(const char *objtype, const char *labelname,
390 bool labeled, int depth, ExplainState *es)
391{
392 switch (es->format)
393 {
394 case EXPLAIN_FORMAT_TEXT:
395 /* nothing to do */
396 break;
397
398 case EXPLAIN_FORMAT_XML:
399 es->indent += depth;
400 break;
401
402 case EXPLAIN_FORMAT_JSON:
403 es->grouping_stack = lcons_int(0, es->grouping_stack);
404 es->indent += depth;
405 break;
406
407 case EXPLAIN_FORMAT_YAML:
408 if (labelname)
409 es->grouping_stack = lcons_int(1, es->grouping_stack);
410 else
411 es->grouping_stack = lcons_int(0, es->grouping_stack);
412 es->indent += depth;
413 break;
414 }
415}
416
417/*
418 * Pop one level of grouping state, allowing for a re-push later.
419 *
420 * This is typically used after ExplainOpenSetAsideGroup; pass the
421 * same "depth" used for that.
422 *
423 * This should not emit any output. If state needs to be saved,
424 * save it at *state_save. Currently, an integer save area is sufficient
425 * for all formats, but we might need to revisit that someday.
426 */
427void
428 ExplainSaveGroup(ExplainState *es, int depth, int *state_save)
429{
430 switch (es->format)
431 {
432 case EXPLAIN_FORMAT_TEXT:
433 /* nothing to do */
434 break;
435
436 case EXPLAIN_FORMAT_XML:
437 es->indent -= depth;
438 break;
439
440 case EXPLAIN_FORMAT_JSON:
441 es->indent -= depth;
442 *state_save = linitial_int(es->grouping_stack);
443 es->grouping_stack = list_delete_first(es->grouping_stack);
444 break;
445
446 case EXPLAIN_FORMAT_YAML:
447 es->indent -= depth;
448 *state_save = linitial_int(es->grouping_stack);
449 es->grouping_stack = list_delete_first(es->grouping_stack);
450 break;
451 }
452}
453
454/*
455 * Re-push one level of grouping state, undoing the effects of ExplainSaveGroup.
456 */
457void
458 ExplainRestoreGroup(ExplainState *es, int depth, int *state_save)
459{
460 switch (es->format)
461 {
462 case EXPLAIN_FORMAT_TEXT:
463 /* nothing to do */
464 break;
465
466 case EXPLAIN_FORMAT_XML:
467 es->indent += depth;
468 break;
469
470 case EXPLAIN_FORMAT_JSON:
471 es->grouping_stack = lcons_int(*state_save, es->grouping_stack);
472 es->indent += depth;
473 break;
474
475 case EXPLAIN_FORMAT_YAML:
476 es->grouping_stack = lcons_int(*state_save, es->grouping_stack);
477 es->indent += depth;
478 break;
479 }
480}
481
482/*
483 * Emit a "dummy" group that never has any members.
484 *
485 * objtype is the type of the group object, labelname is its label within
486 * a containing object (if any).
487 */
488void
489 ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
490{
491 switch (es->format)
492 {
493 case EXPLAIN_FORMAT_TEXT:
494 /* nothing to do */
495 break;
496
497 case EXPLAIN_FORMAT_XML:
498 ExplainXMLTag(objtype, X_CLOSE_IMMEDIATE, es);
499 break;
500
501 case EXPLAIN_FORMAT_JSON:
502 ExplainJSONLineEnding(es);
503 appendStringInfoSpaces(es->str, 2 * es->indent);
504 if (labelname)
505 {
506 escape_json(es->str, labelname);
507 appendStringInfoString(es->str, ": ");
508 }
509 escape_json(es->str, objtype);
510 break;
511
512 case EXPLAIN_FORMAT_YAML:
513 ExplainYAMLLineStarting(es);
514 if (labelname)
515 {
516 escape_yaml(es->str, labelname);
517 appendStringInfoString(es->str, ": ");
518 }
519 else
520 {
521 appendStringInfoString(es->str, "- ");
522 }
523 escape_yaml(es->str, objtype);
524 break;
525 }
526}
527
528/*
529 * Emit the start-of-output boilerplate.
530 *
531 * This is just enough different from processing a subgroup that we need
532 * a separate pair of subroutines.
533 */
534void
535 ExplainBeginOutput(ExplainState *es)
536{
537 switch (es->format)
538 {
539 case EXPLAIN_FORMAT_TEXT:
540 /* nothing to do */
541 break;
542
543 case EXPLAIN_FORMAT_XML:
544 appendStringInfoString(es->str,
545 "<explain xmlns=\"http://www.postgresql.org/2009/explain\">\n");
546 es->indent++;
547 break;
548
549 case EXPLAIN_FORMAT_JSON:
550 /* top-level structure is an array of plans */
551 appendStringInfoChar(es->str, '[');
552 es->grouping_stack = lcons_int(0, es->grouping_stack);
553 es->indent++;
554 break;
555
556 case EXPLAIN_FORMAT_YAML:
557 es->grouping_stack = lcons_int(0, es->grouping_stack);
558 break;
559 }
560}
561
562/*
563 * Emit the end-of-output boilerplate.
564 */
565void
566 ExplainEndOutput(ExplainState *es)
567{
568 switch (es->format)
569 {
570 case EXPLAIN_FORMAT_TEXT:
571 /* nothing to do */
572 break;
573
574 case EXPLAIN_FORMAT_XML:
575 es->indent--;
576 appendStringInfoString(es->str, "</explain>");
577 break;
578
579 case EXPLAIN_FORMAT_JSON:
580 es->indent--;
581 appendStringInfoString(es->str, "\n]");
582 es->grouping_stack = list_delete_first(es->grouping_stack);
583 break;
584
585 case EXPLAIN_FORMAT_YAML:
586 es->grouping_stack = list_delete_first(es->grouping_stack);
587 break;
588 }
589}
590
591/*
592 * Put an appropriate separator between multiple plans
593 */
594void
595 ExplainSeparatePlans(ExplainState *es)
596{
597 switch (es->format)
598 {
599 case EXPLAIN_FORMAT_TEXT:
600 /* add a blank line */
601 appendStringInfoChar(es->str, '\n');
602 break;
603
604 case EXPLAIN_FORMAT_XML:
605 case EXPLAIN_FORMAT_JSON:
606 case EXPLAIN_FORMAT_YAML:
607 /* nothing to do */
608 break;
609 }
610}
611
612/*
613 * Emit opening or closing XML tag.
614 *
615 * "flags" must contain X_OPENING, X_CLOSING, or X_CLOSE_IMMEDIATE.
616 * Optionally, OR in X_NOWHITESPACE to suppress the whitespace we'd normally
617 * add.
618 *
619 * XML restricts tag names more than our other output formats, eg they can't
620 * contain white space or slashes. Replace invalid characters with dashes,
621 * so that for example "I/O Read Time" becomes "I-O-Read-Time".
622 */
623static void
624 ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
625{
626 const char *s;
627 const char *valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.";
628
629 if ((flags & X_NOWHITESPACE) == 0)
630 appendStringInfoSpaces(es->str, 2 * es->indent);
631 appendStringInfoCharMacro(es->str, '<');
632 if ((flags & X_CLOSING) != 0)
633 appendStringInfoCharMacro(es->str, '/');
634 for (s = tagname; *s; s++)
635 appendStringInfoChar(es->str, strchr(valid, *s) ? *s : '-');
636 if ((flags & X_CLOSE_IMMEDIATE) != 0)
637 appendStringInfoString(es->str, " /");
638 appendStringInfoCharMacro(es->str, '>');
639 if ((flags & X_NOWHITESPACE) == 0)
640 appendStringInfoCharMacro(es->str, '\n');
641}
642
643/*
644 * Indent a text-format line.
645 *
646 * We indent by two spaces per indentation level. However, when emitting
647 * data for a parallel worker there might already be data on the current line
648 * (cf. ExplainOpenWorker); in that case, don't indent any more.
649 */
650void
651 ExplainIndentText(ExplainState *es)
652{
653 Assert(es->format == EXPLAIN_FORMAT_TEXT);
654 if (es->str->len == 0 || es->str->data[es->str->len - 1] == '\n')
655 appendStringInfoSpaces(es->str, es->indent * 2);
656}
657
658/*
659 * Emit a JSON line ending.
660 *
661 * JSON requires a comma after each property but the last. To facilitate this,
662 * in JSON format, the text emitted for each property begins just prior to the
663 * preceding line-break (and comma, if applicable).
664 */
665static void
666 ExplainJSONLineEnding(ExplainState *es)
667{
668 Assert(es->format == EXPLAIN_FORMAT_JSON);
669 if (linitial_int(es->grouping_stack) != 0)
670 appendStringInfoChar(es->str, ',');
671 else
672 linitial_int(es->grouping_stack) = 1;
673 appendStringInfoChar(es->str, '\n');
674}
675
676/*
677 * Indent a YAML line.
678 *
679 * YAML lines are ordinarily indented by two spaces per indentation level.
680 * The text emitted for each property begins just prior to the preceding
681 * line-break, except for the first property in an unlabeled group, for which
682 * it begins immediately after the "- " that introduces the group. The first
683 * property of the group appears on the same line as the opening "- ".
684 */
685static void
686 ExplainYAMLLineStarting(ExplainState *es)
687{
688 Assert(es->format == EXPLAIN_FORMAT_YAML);
689 if (linitial_int(es->grouping_stack) == 0)
690 {
691 linitial_int(es->grouping_stack) = 1;
692 }
693 else
694 {
695 appendStringInfoChar(es->str, '\n');
696 appendStringInfoSpaces(es->str, es->indent * 2);
697 }
698}
699
700/*
701 * YAML is a superset of JSON; unfortunately, the YAML quoting rules are
702 * ridiculously complicated -- as documented in sections 5.3 and 7.3.3 of
703 * http://yaml.org/spec/1.2/spec.html -- so we chose to just quote everything.
704 * Empty strings, strings with leading or trailing whitespace, and strings
705 * containing a variety of special characters must certainly be quoted or the
706 * output is invalid; and other seemingly harmless strings like "0xa" or
707 * "true" must be quoted, lest they be interpreted as a hexadecimal or Boolean
708 * constant rather than a string.
709 */
710static void
711 escape_yaml(StringInfo buf, const char *str)
712{
713 escape_json(buf, str);
714}
#define INT64_FORMAT
Definition: c.h:556
int64_t int64
Definition: c.h:535
#define UINT64_FORMAT
Definition: c.h:557
uint64_t uint64
Definition: c.h:539
#define X_OPENING
Definition: explain_format.c:23
void ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
void ExplainOpenGroup(const char *objtype, const char *labelname, bool labeled, ExplainState *es)
#define X_CLOSE_IMMEDIATE
Definition: explain_format.c:25
void ExplainPropertyUInteger(const char *qlabel, const char *unit, uint64 value, ExplainState *es)
#define X_NOWHITESPACE
Definition: explain_format.c:26
void ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value, ExplainState *es)
void ExplainPropertyListNested(const char *qlabel, List *data, ExplainState *es)
static void ExplainYAMLLineStarting(ExplainState *es)
void ExplainOpenSetAsideGroup(const char *objtype, const char *labelname, bool labeled, int depth, ExplainState *es)
void ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
void ExplainSeparatePlans(ExplainState *es)
void ExplainEndOutput(ExplainState *es)
static void ExplainJSONLineEnding(ExplainState *es)
void ExplainIndentText(ExplainState *es)
static void ExplainProperty(const char *qlabel, const char *unit, const char *value, bool numeric, ExplainState *es)
void ExplainPropertyFloat(const char *qlabel, const char *unit, double value, int ndigits, ExplainState *es)
void ExplainCloseGroup(const char *objtype, const char *labelname, bool labeled, ExplainState *es)
void ExplainBeginOutput(ExplainState *es)
static void escape_yaml(StringInfo buf, const char *str)
static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
void ExplainPropertyBool(const char *qlabel, bool value, ExplainState *es)
#define X_CLOSING
Definition: explain_format.c:24
void ExplainRestoreGroup(ExplainState *es, int depth, int *state_save)
void ExplainSaveGroup(ExplainState *es, int depth, int *state_save)
void ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
Definition: explain_format.c:38
@ 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))
const char * str
static struct @169 value
void escape_json(StringInfo buf, const char *str)
Definition: json.c:1603
List * list_delete_first(List *list)
Definition: list.c:943
List * lcons_int(int datum, List *list)
Definition: list.c:513
void pfree(void *pointer)
Definition: mcxt.c:1594
const void * data
#define lfirst(lc)
Definition: pg_list.h:172
#define linitial_int(l)
Definition: pg_list.h:179
static char * buf
Definition: pg_test_fsync.c:72
#define snprintf
Definition: port.h:239
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:145
void appendStringInfoSpaces(StringInfo str, int count)
Definition: stringinfo.c:260
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:230
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:242
#define appendStringInfoCharMacro(str, ch)
Definition: stringinfo.h:231
List * grouping_stack
Definition: explain_state.h:62
StringInfo str
Definition: explain_state.h:46
ExplainFormat format
Definition: explain_state.h:59
Definition: pg_list.h:54
char * data
Definition: stringinfo.h:48
Definition: pg_list.h:46
char * escape_xml(const char *str)
Definition: xml.c:2735

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