PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_overexplain.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pg_overexplain.c
4 * allow EXPLAIN to dump even more details
5 *
6 * Copyright (c) 2016-2026, PostgreSQL Global Development Group
7 *
8 * contrib/pg_overexplain/pg_overexplain.c
9 *-------------------------------------------------------------------------
10 */
11#include "postgres.h"
12
13#include "catalog/pg_class.h"
14#include "commands/defrem.h"
15#include "commands/explain.h"
18#include "fmgr.h"
19#include "parser/parsetree.h"
20#include "storage/lock.h"
21#include "utils/builtins.h"
22#include "utils/lsyscache.h"
23
25 .name = "pg_overexplain",
26 .version = PG_VERSION
27);
28
29typedef struct
30{
31 bool debug;
34
37 ParseState *pstate);
39 ParseState *pstate);
40static void overexplain_per_node_hook(PlanState *planstate, List *ancestors,
41 const char *relationship,
42 const char *plan_name,
43 ExplainState *es);
44static void overexplain_per_plan_hook(PlannedStmt *plannedstmt,
45 IntoClause *into,
46 ExplainState *es,
47 const char *queryString,
48 ParamListInfo params,
49 QueryEnvironment *queryEnv);
50static void overexplain_debug(PlannedStmt *plannedstmt, ExplainState *es);
51static void overexplain_range_table(PlannedStmt *plannedstmt,
52 ExplainState *es);
53static void overexplain_alias(const char *qlabel, Alias *alias,
54 ExplainState *es);
55static void overexplain_bitmapset(const char *qlabel, Bitmapset *bms,
56 ExplainState *es);
57static void overexplain_bitmapset_list(const char *qlabel, List *bms_list,
58 ExplainState *es);
59static void overexplain_intlist(const char *qlabel, List *list,
60 ExplainState *es);
61
62static int es_extension_id;
65
66/*
67 * Initialization we do when this module is loaded.
68 */
69void
71{
72 /* Get an ID that we can use to cache data in an ExplainState. */
73 es_extension_id = GetExplainExtensionId("pg_overexplain");
74
75 /* Register the new EXPLAIN options implemented by this module. */
81
82 /* Use the per-node and per-plan hooks to make our options do something. */
87}
88
89/*
90 * Get the overexplain_options structure from an ExplainState; if there is
91 * none, create one, attach it to the ExplainState, and return it.
92 */
108
109/*
110 * Parse handler for EXPLAIN (DEBUG).
111 */
112static void
119
120/*
121 * Parse handler for EXPLAIN (RANGE_TABLE).
122 */
123static void
131
132/*
133 * Print out additional per-node information as appropriate. If the user didn't
134 * specify any of the options we support, do nothing; else, print whatever is
135 * relevant to the specified options.
136 */
137static void
139 const char *relationship, const char *plan_name,
140 ExplainState *es)
141{
143 Plan *plan = planstate->plan;
144
146 (*prev_explain_per_node_hook) (planstate, ancestors, relationship,
147 plan_name, es);
148
150 if (options == NULL)
151 return;
152
153 /*
154 * If the "debug" option was given, display miscellaneous fields from the
155 * "Plan" node that would not otherwise be displayed.
156 */
157 if (options->debug)
158 {
159 /*
160 * Normal EXPLAIN will display "Disabled: true" if the node is
161 * disabled; but that is based on noticing that plan->disabled_nodes
162 * is higher than the sum of its children; here, we display the raw
163 * value, for debugging purposes.
164 */
165 ExplainPropertyInteger("Disabled Nodes", NULL, plan->disabled_nodes,
166 es);
167
168 /*
169 * Normal EXPLAIN will display the parallel_aware flag; here, we show
170 * the parallel_safe flag as well.
171 */
172 ExplainPropertyBool("Parallel Safe", plan->parallel_safe, es);
173
174 /*
175 * The plan node ID isn't normally displayed, since it is only useful
176 * for debugging.
177 */
178 ExplainPropertyInteger("Plan Node ID", NULL, plan->plan_node_id, es);
179
180 /*
181 * It is difficult to explain what extParam and allParam mean in plain
182 * language, so we simply display these fields labelled with the
183 * structure member name. For compactness, the text format omits the
184 * display of this information when the bitmapset is empty.
185 */
186 if (es->format != EXPLAIN_FORMAT_TEXT || !bms_is_empty(plan->extParam))
187 overexplain_bitmapset("extParam", plan->extParam, es);
188 if (es->format != EXPLAIN_FORMAT_TEXT || !bms_is_empty(plan->allParam))
189 overexplain_bitmapset("allParam", plan->allParam, es);
190 }
191
192 /*
193 * If the "range_table" option was specified, display information about
194 * the range table indexes for this node.
195 */
196 if (options->range_table)
197 {
198 bool opened_elided_nodes = false;
199
200 switch (nodeTag(plan))
201 {
202 case T_SeqScan:
203 case T_SampleScan:
204 case T_IndexScan:
205 case T_IndexOnlyScan:
206 case T_BitmapHeapScan:
207 case T_TidScan:
208 case T_TidRangeScan:
209 case T_SubqueryScan:
210 case T_FunctionScan:
211 case T_TableFuncScan:
212 case T_ValuesScan:
213 case T_CteScan:
215 case T_WorkTableScan:
216 ExplainPropertyInteger("Scan RTI", NULL,
217 ((Scan *) plan)->scanrelid, es);
218 break;
219 case T_ForeignScan:
220 overexplain_bitmapset("Scan RTIs",
221 ((ForeignScan *) plan)->fs_base_relids,
222 es);
223 break;
224 case T_CustomScan:
225 overexplain_bitmapset("Scan RTIs",
226 ((CustomScan *) plan)->custom_relids,
227 es);
228 break;
229 case T_ModifyTable:
230 ExplainPropertyInteger("Nominal RTI", NULL,
231 ((ModifyTable *) plan)->nominalRelation, es);
232 ExplainPropertyInteger("Exclude Relation RTI", NULL,
233 ((ModifyTable *) plan)->exclRelRTI, es);
234 break;
235 case T_Append:
236 overexplain_bitmapset("Append RTIs",
237 ((Append *) plan)->apprelids,
238 es);
239 overexplain_bitmapset_list("Child Append RTIs",
240 ((Append *) plan)->child_append_relid_sets,
241 es);
242 break;
243 case T_MergeAppend:
244 overexplain_bitmapset("Append RTIs",
245 ((MergeAppend *) plan)->apprelids,
246 es);
247 overexplain_bitmapset_list("Child Append RTIs",
248 ((MergeAppend *) plan)->child_append_relid_sets,
249 es);
250 break;
251 case T_Result:
252
253 /*
254 * 'relids' is only meaningful when plan->lefttree is NULL,
255 * but if somehow it ends up set when plan->lefttree is not
256 * NULL, print it anyway.
257 */
258 if (plan->lefttree == NULL ||
259 ((Result *) plan)->relids != NULL)
261 ((Result *) plan)->relids,
262 es);
263 break;
264 default:
265 break;
266 }
267
269 {
270 char *elidednodetag;
271
272 if (n->plan_node_id != plan->plan_node_id)
273 continue;
274
276 {
277 ExplainOpenGroup("Elided Nodes", "Elided Nodes", false, es);
278 opened_elided_nodes = true;
279 }
280
281 switch (n->elided_type)
282 {
283 case T_Append:
284 elidednodetag = "Append";
285 break;
286 case T_MergeAppend:
287 elidednodetag = "MergeAppend";
288 break;
289 case T_SubqueryScan:
290 elidednodetag = "SubqueryScan";
291 break;
292 default:
293 elidednodetag = psprintf("%d", n->elided_type);
294 break;
295 }
296
297 ExplainOpenGroup("Elided Node", NULL, true, es);
298 ExplainPropertyText("Elided Node Type", elidednodetag, es);
299 overexplain_bitmapset("Elided Node RTIs", n->relids, es);
300 ExplainCloseGroup("Elided Node", NULL, true, es);
301 }
303 ExplainCloseGroup("Elided Nodes", "Elided Nodes", false, es);
304 }
305}
306
307/*
308 * Print out additional per-query information as appropriate. Here again, if
309 * the user didn't specify any of the options implemented by this module, do
310 * nothing; otherwise, call the appropriate function for each specified
311 * option.
312 */
313static void
315 IntoClause *into,
316 ExplainState *es,
317 const char *queryString,
318 ParamListInfo params,
319 QueryEnvironment *queryEnv)
320{
322
324 (*prev_explain_per_plan_hook) (plannedstmt, into, es, queryString,
325 params, queryEnv);
326
328 if (options == NULL)
329 return;
330
331 if (options->debug)
332 overexplain_debug(plannedstmt, es);
333
334 if (options->range_table)
335 overexplain_range_table(plannedstmt, es);
336}
337
338/*
339 * Print out various details from the PlannedStmt that wouldn't otherwise
340 * be displayed.
341 *
342 * We don't try to print everything here. Information that would be displayed
343 * anyway doesn't need to be printed again here, and things with lots of
344 * substructure probably should be printed via separate options, or not at all.
345 */
346static void
348{
349 char *commandType = NULL;
350 StringInfoData flags;
351
352 /* Even in text mode, we want to set this output apart as its own group. */
353 ExplainOpenGroup("PlannedStmt", "PlannedStmt", true, es);
354 if (es->format == EXPLAIN_FORMAT_TEXT)
355 {
357 appendStringInfoString(es->str, "PlannedStmt:\n");
358 es->indent++;
359 }
360
361 /* Print the command type. */
362 switch (plannedstmt->commandType)
363 {
364 case CMD_UNKNOWN:
365 commandType = "unknown";
366 break;
367 case CMD_SELECT:
368 commandType = "select";
369 break;
370 case CMD_UPDATE:
371 commandType = "update";
372 break;
373 case CMD_INSERT:
374 commandType = "insert";
375 break;
376 case CMD_DELETE:
377 commandType = "delete";
378 break;
379 case CMD_MERGE:
380 commandType = "merge";
381 break;
382 case CMD_UTILITY:
383 commandType = "utility";
384 break;
385 case CMD_NOTHING:
386 commandType = "nothing";
387 break;
388 }
389 ExplainPropertyText("Command Type", commandType, es);
390
391 /* Print various properties as a comma-separated list of flags. */
392 initStringInfo(&flags);
393 if (plannedstmt->hasReturning)
394 appendStringInfoString(&flags, ", hasReturning");
395 if (plannedstmt->hasModifyingCTE)
396 appendStringInfoString(&flags, ", hasModifyingCTE");
397 if (plannedstmt->canSetTag)
398 appendStringInfoString(&flags, ", canSetTag");
399 if (plannedstmt->transientPlan)
400 appendStringInfoString(&flags, ", transientPlan");
401 if (plannedstmt->dependsOnRole)
402 appendStringInfoString(&flags, ", dependsOnRole");
403 if (plannedstmt->parallelModeNeeded)
404 appendStringInfoString(&flags, ", parallelModeNeeded");
405 if (flags.len == 0)
406 appendStringInfoString(&flags, ", none");
407 ExplainPropertyText("Flags", flags.data + 2, es);
408
409 /* Various lists of integers. */
410 overexplain_bitmapset("Subplans Needing Rewind",
411 plannedstmt->rewindPlanIDs, es);
412 overexplain_intlist("Relation OIDs",
413 plannedstmt->relationOids, es);
414 overexplain_intlist("Executor Parameter Types",
415 plannedstmt->paramExecTypes, es);
416
417 /*
418 * Print the statement location. (If desired, we could alternatively print
419 * stmt_location and stmt_len as two separate fields.)
420 */
421 if (plannedstmt->stmt_location == -1)
422 ExplainPropertyText("Parse Location", "Unknown", es);
423 else if (plannedstmt->stmt_len == 0)
424 ExplainPropertyText("Parse Location",
425 psprintf("%d to end", plannedstmt->stmt_location),
426 es);
427 else
428 ExplainPropertyText("Parse Location",
429 psprintf("%d for %d bytes",
430 plannedstmt->stmt_location,
431 plannedstmt->stmt_len),
432 es);
433
434 /* Done with this group. */
435 if (es->format == EXPLAIN_FORMAT_TEXT)
436 es->indent--;
437 ExplainCloseGroup("PlannedStmt", "PlannedStmt", true, es);
438}
439
440/*
441 * Provide detailed information about the contents of the PlannedStmt's
442 * range table.
443 */
444static void
446{
447 Index rti;
448 ListCell *lc_subrtinfo = list_head(plannedstmt->subrtinfos);
450
451 /* Open group, one entry per RangeTblEntry */
452 ExplainOpenGroup("Range Table", "Range Table", false, es);
453
454 /* Iterate over the range table */
455 for (rti = 1; rti <= list_length(plannedstmt->rtable); ++rti)
456 {
457 RangeTblEntry *rte = rt_fetch(rti, plannedstmt->rtable);
458 char *kind = NULL;
459 char *relkind;
461
462 /* Advance to next SubRTInfo, if it's time. */
463 if (lc_subrtinfo != NULL)
464 {
466 if (rti > next_rtinfo->rtoffset)
467 {
469 lc_subrtinfo = lnext(plannedstmt->subrtinfos, lc_subrtinfo);
470 }
471 }
472
473 /* NULL entries are possible; skip them */
474 if (rte == NULL)
475 continue;
476
477 /* Translate rtekind to a string */
478 switch (rte->rtekind)
479 {
480 case RTE_RELATION:
481 kind = "relation";
482 break;
483 case RTE_SUBQUERY:
484 kind = "subquery";
485 break;
486 case RTE_JOIN:
487 kind = "join";
488 break;
489 case RTE_FUNCTION:
490 kind = "function";
491 break;
492 case RTE_TABLEFUNC:
493 kind = "tablefunc";
494 break;
495 case RTE_VALUES:
496 kind = "values";
497 break;
498 case RTE_CTE:
499 kind = "cte";
500 break;
502 kind = "namedtuplestore";
503 break;
504 case RTE_RESULT:
505 kind = "result";
506 break;
507 case RTE_GROUP:
508 kind = "group";
509 break;
510 case RTE_GRAPH_TABLE:
511
512 /*
513 * We should not see RTE of this kind here since property
514 * graph RTE gets converted to subquery RTE in
515 * RewriteGraphTable(). In case we decide not to do the
516 * conversion and leave RTEkind unchanged in future, print
517 * correct name of RTE kind.
518 */
519 kind = "graph_table";
520 break;
521 }
522
523 /* Begin group for this specific RTE */
524 ExplainOpenGroup("Range Table Entry", NULL, true, es);
525
526 /*
527 * In text format, the summary line displays the range table index and
528 * rtekind, plus indications if rte->inh and/or rte->inFromCl are set.
529 * In other formats, we display those as separate properties.
530 */
531 if (es->format == EXPLAIN_FORMAT_TEXT)
532 {
534 appendStringInfo(es->str, "RTI %u (%s%s%s):\n", rti, kind,
535 rte->inh ? ", inherited" : "",
536 rte->inFromCl ? ", in-from-clause" : "");
537 es->indent++;
538 }
539 else
540 {
541 ExplainPropertyUInteger("RTI", NULL, rti, es);
542 ExplainPropertyText("Kind", kind, es);
543 ExplainPropertyBool("Inherited", rte->inh, es);
544 ExplainPropertyBool("In From Clause", rte->inFromCl, es);
545 }
546
547 /*
548 * Indicate which subplan is the origin of which RTE. Note dummy
549 * subplans. Here again, we crunch more onto one line in text format.
550 */
551 if (rtinfo != NULL)
552 {
553 if (es->format == EXPLAIN_FORMAT_TEXT)
554 {
555 if (!rtinfo->dummy)
556 ExplainPropertyText("Subplan", rtinfo->plan_name, es);
557 else
558 ExplainPropertyText("Subplan",
559 psprintf("%s (dummy)",
560 rtinfo->plan_name), es);
561 }
562 else
563 {
564 ExplainPropertyText("Subplan", rtinfo->plan_name, es);
565 ExplainPropertyBool("Subplan Is Dummy", rtinfo->dummy, es);
566 }
567 }
568
569 /* rte->alias is optional; rte->eref is requested */
570 if (rte->alias != NULL)
571 overexplain_alias("Alias", rte->alias, es);
572 overexplain_alias("Eref", rte->eref, es);
573
574 /*
575 * We adhere to the usual EXPLAIN convention that schema names are
576 * displayed only in verbose mode, and we emit nothing if there is no
577 * relation OID.
578 */
579 if (rte->relid != 0)
580 {
581 const char *relname;
582 const char *qualname;
583
585
586 if (es->verbose)
587 {
589 char *nspname;
590
592 qualname = psprintf("%s.%s", quote_identifier(nspname),
593 relname);
594 }
595 else
597
598 ExplainPropertyText("Relation", qualname, es);
599 }
600
601 /* Translate relkind, if any, to a string */
602 switch (rte->relkind)
603 {
604 case RELKIND_RELATION:
605 relkind = "relation";
606 break;
607 case RELKIND_INDEX:
608 relkind = "index";
609 break;
610 case RELKIND_SEQUENCE:
611 relkind = "sequence";
612 break;
614 relkind = "toastvalue";
615 break;
616 case RELKIND_VIEW:
617 relkind = "view";
618 break;
619 case RELKIND_MATVIEW:
620 relkind = "matview";
621 break;
623 relkind = "composite_type";
624 break;
626 relkind = "foreign_table";
627 break;
629 relkind = "partitioned_table";
630 break;
632 relkind = "partitioned_index";
633 break;
635 relkind = "property_graph";
636 break;
637 case '\0':
638 relkind = NULL;
639 break;
640 default:
641 relkind = psprintf("%c", rte->relkind);
642 break;
643 }
644
645 /* If there is a relkind, show it */
646 if (relkind != NULL)
647 ExplainPropertyText("Relation Kind", relkind, es);
648
649 /* If there is a lock mode, show it */
650 if (rte->rellockmode != 0)
651 ExplainPropertyText("Relation Lock Mode",
653 rte->rellockmode), es);
654
655 /*
656 * If there is a perminfoindex, show it. We don't try to display
657 * information from the RTEPermissionInfo node here because they are
658 * just indexes plannedstmt->permInfos which could be separately
659 * dumped if someone wants to add EXPLAIN (PERMISSIONS) or similar.
660 */
661 if (rte->perminfoindex != 0)
662 ExplainPropertyInteger("Permission Info Index", NULL,
663 rte->perminfoindex, es);
664
665 /*
666 * add_rte_to_flat_rtable will clear rte->tablesample and
667 * rte->subquery in the finished plan, so skip those fields.
668 *
669 * However, the security_barrier flag is not shown by the core code,
670 * so let's print it here.
671 */
672 if (es->format != EXPLAIN_FORMAT_TEXT || rte->security_barrier)
673 ExplainPropertyBool("Security Barrier", rte->security_barrier, es);
674
675 /*
676 * If this is a join, print out the fields that are specifically valid
677 * for joins.
678 */
679 if (rte->rtekind == RTE_JOIN)
680 {
681 char *jointype;
682
683 switch (rte->jointype)
684 {
685 case JOIN_INNER:
686 jointype = "Inner";
687 break;
688 case JOIN_LEFT:
689 jointype = "Left";
690 break;
691 case JOIN_FULL:
692 jointype = "Full";
693 break;
694 case JOIN_RIGHT:
695 jointype = "Right";
696 break;
697 case JOIN_SEMI:
698 jointype = "Semi";
699 break;
700 case JOIN_ANTI:
701 jointype = "Anti";
702 break;
703 case JOIN_RIGHT_SEMI:
704 jointype = "Right Semi";
705 break;
706 case JOIN_RIGHT_ANTI:
707 jointype = "Right Anti";
708 break;
709 default:
710 jointype = "???";
711 break;
712 }
713
714 /* Join type */
715 ExplainPropertyText("Join Type", jointype, es);
716
717 /* # of JOIN USING columns */
718 if (es->format != EXPLAIN_FORMAT_TEXT || rte->joinmergedcols != 0)
719 ExplainPropertyInteger("JOIN USING Columns", NULL,
720 rte->joinmergedcols, es);
721
722 /*
723 * add_rte_to_flat_rtable will clear joinaliasvars, joinleftcols,
724 * joinrightcols, and join_using_alias here, so skip those fields.
725 */
726 }
727
728 /*
729 * add_rte_to_flat_rtable will clear functions, tablefunc, and
730 * values_lists, but we can display funcordinality.
731 */
732 if (rte->rtekind == RTE_FUNCTION)
733 ExplainPropertyBool("WITH ORDINALITY", rte->funcordinality, es);
734
735 /*
736 * If this is a CTE, print out CTE-related properties.
737 */
738 if (rte->rtekind == RTE_CTE)
739 {
740 ExplainPropertyText("CTE Name", rte->ctename, es);
741 ExplainPropertyUInteger("CTE Levels Up", NULL, rte->ctelevelsup,
742 es);
743 ExplainPropertyBool("CTE Self-Reference", rte->self_reference, es);
744 }
745
746 /*
747 * add_rte_to_flat_rtable will clear coltypes, coltypmods, and
748 * colcollations, so skip those fields.
749 *
750 * If this is an ephemeral named relation, print out ENR-related
751 * properties.
752 */
753 if (rte->rtekind == RTE_NAMEDTUPLESTORE)
754 {
755 ExplainPropertyText("ENR Name", rte->enrname, es);
756 ExplainPropertyFloat("ENR Tuples", NULL, rte->enrtuples, 0, es);
757 }
758
759 /*
760 * rewriteGraphTable() clears graph_pattern and graph_table_columns
761 * fields, so skip them. No graph table specific fields are required
762 * to be printed.
763 */
764
765 /*
766 * add_rte_to_flat_rtable will clear groupexprs and securityQuals, so
767 * skip that field. We have handled inFromCl above, so the only thing
768 * left to handle here is rte->lateral.
769 */
770 if (es->format != EXPLAIN_FORMAT_TEXT || rte->lateral)
771 ExplainPropertyBool("Lateral", rte->lateral, es);
772
773 /* Done with this RTE */
774 if (es->format == EXPLAIN_FORMAT_TEXT)
775 es->indent--;
776 ExplainCloseGroup("Range Table Entry", NULL, true, es);
777 }
778
779 /* Print PlannedStmt fields that contain RTIs. */
780 if (es->format != EXPLAIN_FORMAT_TEXT ||
781 !bms_is_empty(plannedstmt->unprunableRelids))
782 overexplain_bitmapset("Unprunable RTIs", plannedstmt->unprunableRelids,
783 es);
784 if (es->format != EXPLAIN_FORMAT_TEXT ||
785 !bms_is_empty(plannedstmt->resultRelationRelids))
786 overexplain_bitmapset("Result RTIs", plannedstmt->resultRelationRelids,
787 es);
788
789 /* Close group, we're all done */
790 ExplainCloseGroup("Range Table", "Range Table", false, es);
791}
792
793/*
794 * Emit a text property describing the contents of an Alias.
795 *
796 * Column lists can be quite long here, so perhaps we should have an option
797 * to limit the display length by # of column or # of characters, but for
798 * now, just display everything.
799 */
800static void
802{
804 bool first = true;
805
806 Assert(alias != NULL);
807
810
812 {
813 appendStringInfo(&buf, "%s%s",
814 first ? "" : ", ",
815 quote_identifier(cn->sval));
816 first = false;
817 }
818
820 ExplainPropertyText(qlabel, buf.data, es);
821 pfree(buf.data);
822}
823
824/*
825 * Emit a text property describing the contents of a bitmapset -- either a
826 * space-separated list of integer members, or the word "none" if the bitmapset
827 * is empty.
828 */
829static void
831{
832 int x = -1;
833
835
836 if (bms_is_empty(bms))
837 {
838 ExplainPropertyText(qlabel, "none", es);
839 return;
840 }
841
843 while ((x = bms_next_member(bms, x)) >= 0)
844 appendStringInfo(&buf, " %d", x);
845 Assert(buf.data[0] == ' ');
846 ExplainPropertyText(qlabel, buf.data + 1, es);
847 pfree(buf.data);
848}
849
850/*
851 * Emit a text property describing the contents of a list of bitmapsets.
852 * If a bitmapset contains exactly 1 member, we just print an integer;
853 * otherwise, we surround the list of members by parentheses.
854 *
855 * If there are no bitmapsets in the list, we print the word "none".
856 */
857static void
859 ExplainState *es)
860{
862
864
866 {
869 else
870 {
871 int x = -1;
872 bool first = true;
873
875 while ((x = bms_next_member(bms, x)) >= 0)
876 {
877 if (first)
878 first = false;
879 else
881 appendStringInfo(&buf, "%d", x);
882 }
884 }
885 }
886
887 if (buf.len == 0)
888 {
889 ExplainPropertyText(qlabel, "none", es);
890 return;
891 }
892
893 Assert(buf.data[0] == ' ');
894 ExplainPropertyText(qlabel, buf.data + 1, es);
895 pfree(buf.data);
896}
897
898/*
899 * Emit a text property describing the contents of a list of integers, OIDs,
900 * or XIDs -- either a space-separated list of integer members, or the word
901 * "none" if the list is empty.
902 */
903static void
905{
907
909
910 if (list == NIL)
911 {
912 ExplainPropertyText(qlabel, "none", es);
913 return;
914 }
915
916 if (IsA(list, IntList))
917 {
918 foreach_int(i, list)
919 appendStringInfo(&buf, " %d", i);
920 }
921 else if (IsA(list, OidList))
922 {
923 foreach_oid(o, list)
924 appendStringInfo(&buf, " %u", o);
925 }
926 else if (IsA(list, XidList))
927 {
928 foreach_xid(x, list)
929 appendStringInfo(&buf, " %u", x);
930 }
931 else
932 {
933 appendStringInfoString(&buf, " not an integer list");
934 Assert(false);
935 }
936
937 if (buf.len > 0)
938 ExplainPropertyText(qlabel, buf.data + 1, es);
939
940 pfree(buf.data);
941}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1290
int bms_singleton_member(const Bitmapset *a)
Definition bitmapset.c:665
BMS_Membership bms_membership(const Bitmapset *a)
Definition bitmapset.c:765
#define bms_is_empty(a)
Definition bitmapset.h:118
@ BMS_SINGLETON
Definition bitmapset.h:72
#define Assert(condition)
Definition c.h:943
unsigned int Index
Definition c.h:698
bool defGetBoolean(DefElem *def)
Definition define.c:93
explain_per_node_hook_type explain_per_node_hook
Definition explain.c:59
explain_per_plan_hook_type explain_per_plan_hook
Definition explain.c:58
void(* explain_per_plan_hook_type)(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
Definition explain.h:33
void(* explain_per_node_hook_type)(PlanState *planstate, List *ancestors, const char *relationship, const char *plan_name, ExplainState *es)
Definition explain.h:42
void ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
void ExplainOpenGroup(const char *objtype, const char *labelname, bool labeled, ExplainState *es)
void ExplainPropertyUInteger(const char *qlabel, const char *unit, uint64 value, ExplainState *es)
void ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value, ExplainState *es)
void ExplainIndentText(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 ExplainPropertyBool(const char *qlabel, bool value, ExplainState *es)
int GetExplainExtensionId(const char *extension_name)
void * GetExplainExtensionState(ExplainState *es, int extension_id)
void SetExplainExtensionState(ExplainState *es, int extension_id, void *opaque)
void RegisterExtensionExplainOption(const char *option_name, ExplainOptionHandler handler, ExplainOptionGUCCheckHandler guc_check_handler)
bool GUCCheckBooleanExplainOption(const char *option_name, const char *option_value, NodeTag option_type)
@ EXPLAIN_FORMAT_TEXT
#define palloc0_object(type)
Definition fe_memutils.h:75
#define PG_MODULE_MAGIC_EXT(...)
Definition fmgr.h:540
int x
Definition isn.c:75
int i
Definition isn.c:77
const char * GetLockmodeName(LOCKMETHODID lockmethodid, LOCKMODE mode)
Definition lock.c:4236
#define DEFAULT_LOCKMETHOD
Definition locktag.h:25
char * get_rel_name(Oid relid)
Definition lsyscache.c:2148
Oid get_rel_namespace(Oid relid)
Definition lsyscache.c:2172
char * get_namespace_name_or_temp(Oid nspid)
Definition lsyscache.c:3612
void pfree(void *pointer)
Definition mcxt.c:1616
#define IsA(nodeptr, _type_)
Definition nodes.h:164
#define nodeTag(nodeptr)
Definition nodes.h:139
@ CMD_MERGE
Definition nodes.h:279
@ CMD_UTILITY
Definition nodes.h:280
@ CMD_INSERT
Definition nodes.h:277
@ CMD_DELETE
Definition nodes.h:278
@ CMD_UNKNOWN
Definition nodes.h:274
@ CMD_UPDATE
Definition nodes.h:276
@ CMD_SELECT
Definition nodes.h:275
@ CMD_NOTHING
Definition nodes.h:282
@ JOIN_SEMI
Definition nodes.h:317
@ JOIN_FULL
Definition nodes.h:305
@ JOIN_INNER
Definition nodes.h:303
@ JOIN_RIGHT
Definition nodes.h:306
@ JOIN_RIGHT_SEMI
Definition nodes.h:319
@ JOIN_LEFT
Definition nodes.h:304
@ JOIN_RIGHT_ANTI
Definition nodes.h:320
@ JOIN_ANTI
Definition nodes.h:318
@ RTE_JOIN
@ RTE_CTE
@ RTE_NAMEDTUPLESTORE
@ RTE_VALUES
@ RTE_SUBQUERY
@ RTE_RESULT
@ RTE_FUNCTION
@ RTE_TABLEFUNC
@ RTE_GROUP
@ RTE_GRAPH_TABLE
@ RTE_RELATION
#define rt_fetch(rangetable_index, rangetable)
Definition parsetree.h:31
NameData relname
Definition pg_class.h:40
#define lfirst(lc)
Definition pg_list.h:172
static int list_length(const List *l)
Definition pg_list.h:152
#define NIL
Definition pg_list.h:68
#define foreach_xid(var, lst)
Definition pg_list.h:504
#define foreach_node(type, var, lst)
Definition pg_list.h:528
static ListCell * list_head(const List *l)
Definition pg_list.h:128
#define foreach_oid(var, lst)
Definition pg_list.h:503
static ListCell * lnext(const List *l, const ListCell *c)
Definition pg_list.h:375
#define foreach_int(var, lst)
Definition pg_list.h:502
static void overexplain_per_plan_hook(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
void _PG_init(void)
static void overexplain_bitmapset(const char *qlabel, Bitmapset *bms, ExplainState *es)
static void overexplain_range_table(PlannedStmt *plannedstmt, ExplainState *es)
static explain_per_plan_hook_type prev_explain_per_plan_hook
static explain_per_node_hook_type prev_explain_per_node_hook
static overexplain_options * overexplain_ensure_options(ExplainState *es)
static void overexplain_bitmapset_list(const char *qlabel, List *bms_list, ExplainState *es)
static void overexplain_per_node_hook(PlanState *planstate, List *ancestors, const char *relationship, const char *plan_name, ExplainState *es)
static void overexplain_intlist(const char *qlabel, List *list, ExplainState *es)
static void overexplain_debug_handler(ExplainState *es, DefElem *opt, ParseState *pstate)
static void overexplain_alias(const char *qlabel, Alias *alias, ExplainState *es)
static int es_extension_id
static void overexplain_debug(PlannedStmt *plannedstmt, ExplainState *es)
static void overexplain_range_table_handler(ExplainState *es, DefElem *opt, ParseState *pstate)
#define plan(x)
Definition pg_regress.c:164
static char buf[DEFAULT_XLOG_SEG_SIZE]
unsigned int Oid
static int fb(int x)
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
const char * quote_identifier(const char *ident)
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
void appendStringInfoString(StringInfo str, const char *s)
Definition stringinfo.c:230
void appendStringInfoChar(StringInfo str, char ch)
Definition stringinfo.c:242
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
char * aliasname
Definition primnodes.h:52
List * colnames
Definition primnodes.h:53
StringInfo str
ExplainFormat format
PlannedStmt * pstmt
Definition pg_list.h:54
Plan * plan
Definition execnodes.h:1201
bool hasModifyingCTE
Definition plannodes.h:81
List * elidedNodes
Definition plannodes.h:156
bool canSetTag
Definition plannodes.h:84
Bitmapset * rewindPlanIDs
Definition plannodes.h:135
ParseLoc stmt_len
Definition plannodes.h:171
bool hasReturning
Definition plannodes.h:78
ParseLoc stmt_location
Definition plannodes.h:169
Bitmapset * resultRelationRelids
Definition plannodes.h:121
bool transientPlan
Definition plannodes.h:87
List * relationOids
Definition plannodes.h:144
List * subrtinfos
Definition plannodes.h:132
bool dependsOnRole
Definition plannodes.h:90
Bitmapset * unprunableRelids
Definition plannodes.h:113
CmdType commandType
Definition plannodes.h:66
List * rtable
Definition plannodes.h:107
List * paramExecTypes
Definition plannodes.h:150
bool parallelModeNeeded
Definition plannodes.h:93
Definition value.h:64
const char * name