00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "postgres.h"
00015
00016 #include "access/xact.h"
00017 #include "catalog/pg_type.h"
00018 #include "commands/createas.h"
00019 #include "commands/defrem.h"
00020 #include "commands/prepare.h"
00021 #include "executor/hashjoin.h"
00022 #include "foreign/fdwapi.h"
00023 #include "optimizer/clauses.h"
00024 #include "parser/parsetree.h"
00025 #include "rewrite/rewriteHandler.h"
00026 #include "tcop/tcopprot.h"
00027 #include "utils/builtins.h"
00028 #include "utils/json.h"
00029 #include "utils/lsyscache.h"
00030 #include "utils/rel.h"
00031 #include "utils/snapmgr.h"
00032 #include "utils/tuplesort.h"
00033 #include "utils/xml.h"
00034
00035
00036
00037 ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL;
00038
00039
00040 explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
00041
00042
00043
00044 #define X_OPENING 0
00045 #define X_CLOSING 1
00046 #define X_CLOSE_IMMEDIATE 2
00047 #define X_NOWHITESPACE 4
00048
00049 static void ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
00050 const char *queryString, ParamListInfo params);
00051 static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
00052 ExplainState *es);
00053 static double elapsed_time(instr_time *starttime);
00054 static void ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used);
00055 static void ExplainPreScanMemberNodes(List *plans, PlanState **planstates,
00056 Bitmapset **rels_used);
00057 static void ExplainPreScanSubPlans(List *plans, Bitmapset **rels_used);
00058 static void ExplainNode(PlanState *planstate, List *ancestors,
00059 const char *relationship, const char *plan_name,
00060 ExplainState *es);
00061 static void show_plan_tlist(PlanState *planstate, List *ancestors,
00062 ExplainState *es);
00063 static void show_expression(Node *node, const char *qlabel,
00064 PlanState *planstate, List *ancestors,
00065 bool useprefix, ExplainState *es);
00066 static void show_qual(List *qual, const char *qlabel,
00067 PlanState *planstate, List *ancestors,
00068 bool useprefix, ExplainState *es);
00069 static void show_scan_qual(List *qual, const char *qlabel,
00070 PlanState *planstate, List *ancestors,
00071 ExplainState *es);
00072 static void show_upper_qual(List *qual, const char *qlabel,
00073 PlanState *planstate, List *ancestors,
00074 ExplainState *es);
00075 static void show_sort_keys(SortState *sortstate, List *ancestors,
00076 ExplainState *es);
00077 static void show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
00078 ExplainState *es);
00079 static void show_sort_keys_common(PlanState *planstate,
00080 int nkeys, AttrNumber *keycols,
00081 List *ancestors, ExplainState *es);
00082 static void show_sort_info(SortState *sortstate, ExplainState *es);
00083 static void show_hash_info(HashState *hashstate, ExplainState *es);
00084 static void show_instrumentation_count(const char *qlabel, int which,
00085 PlanState *planstate, ExplainState *es);
00086 static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
00087 static const char *explain_get_index_name(Oid indexId);
00088 static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
00089 ExplainState *es);
00090 static void ExplainScanTarget(Scan *plan, ExplainState *es);
00091 static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es);
00092 static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
00093 static void show_modifytable_info(ModifyTableState *mtstate, ExplainState *es);
00094 static void ExplainMemberNodes(List *plans, PlanState **planstates,
00095 List *ancestors, ExplainState *es);
00096 static void ExplainSubPlans(List *plans, List *ancestors,
00097 const char *relationship, ExplainState *es);
00098 static void ExplainProperty(const char *qlabel, const char *value,
00099 bool numeric, ExplainState *es);
00100 static void ExplainOpenGroup(const char *objtype, const char *labelname,
00101 bool labeled, ExplainState *es);
00102 static void ExplainCloseGroup(const char *objtype, const char *labelname,
00103 bool labeled, ExplainState *es);
00104 static void ExplainDummyGroup(const char *objtype, const char *labelname,
00105 ExplainState *es);
00106 static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
00107 static void ExplainJSONLineEnding(ExplainState *es);
00108 static void ExplainYAMLLineStarting(ExplainState *es);
00109 static void escape_yaml(StringInfo buf, const char *str);
00110
00111
00112
00113
00114
00115
00116
00117 void
00118 ExplainQuery(ExplainStmt *stmt, const char *queryString,
00119 ParamListInfo params, DestReceiver *dest)
00120 {
00121 ExplainState es;
00122 TupOutputState *tstate;
00123 List *rewritten;
00124 ListCell *lc;
00125 bool timing_set = false;
00126
00127
00128 ExplainInitState(&es);
00129
00130
00131 foreach(lc, stmt->options)
00132 {
00133 DefElem *opt = (DefElem *) lfirst(lc);
00134
00135 if (strcmp(opt->defname, "analyze") == 0)
00136 es.analyze = defGetBoolean(opt);
00137 else if (strcmp(opt->defname, "verbose") == 0)
00138 es.verbose = defGetBoolean(opt);
00139 else if (strcmp(opt->defname, "costs") == 0)
00140 es.costs = defGetBoolean(opt);
00141 else if (strcmp(opt->defname, "buffers") == 0)
00142 es.buffers = defGetBoolean(opt);
00143 else if (strcmp(opt->defname, "timing") == 0)
00144 {
00145 timing_set = true;
00146 es.timing = defGetBoolean(opt);
00147 }
00148 else if (strcmp(opt->defname, "format") == 0)
00149 {
00150 char *p = defGetString(opt);
00151
00152 if (strcmp(p, "text") == 0)
00153 es.format = EXPLAIN_FORMAT_TEXT;
00154 else if (strcmp(p, "xml") == 0)
00155 es.format = EXPLAIN_FORMAT_XML;
00156 else if (strcmp(p, "json") == 0)
00157 es.format = EXPLAIN_FORMAT_JSON;
00158 else if (strcmp(p, "yaml") == 0)
00159 es.format = EXPLAIN_FORMAT_YAML;
00160 else
00161 ereport(ERROR,
00162 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00163 errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
00164 opt->defname, p)));
00165 }
00166 else
00167 ereport(ERROR,
00168 (errcode(ERRCODE_SYNTAX_ERROR),
00169 errmsg("unrecognized EXPLAIN option \"%s\"",
00170 opt->defname)));
00171 }
00172
00173 if (es.buffers && !es.analyze)
00174 ereport(ERROR,
00175 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00176 errmsg("EXPLAIN option BUFFERS requires ANALYZE")));
00177
00178
00179 es.timing = (timing_set) ? es.timing : es.analyze;
00180
00181
00182 if (es.timing && !es.analyze)
00183 ereport(ERROR,
00184 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00185 errmsg("EXPLAIN option TIMING requires ANALYZE")));
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199 Assert(IsA(stmt->query, Query));
00200 rewritten = QueryRewrite((Query *) copyObject(stmt->query));
00201
00202
00203 ExplainBeginOutput(&es);
00204
00205 if (rewritten == NIL)
00206 {
00207
00208
00209
00210
00211 if (es.format == EXPLAIN_FORMAT_TEXT)
00212 appendStringInfoString(es.str, "Query rewrites to nothing\n");
00213 }
00214 else
00215 {
00216 ListCell *l;
00217
00218
00219 foreach(l, rewritten)
00220 {
00221 ExplainOneQuery((Query *) lfirst(l), NULL, &es,
00222 queryString, params);
00223
00224
00225 if (lnext(l) != NULL)
00226 ExplainSeparatePlans(&es);
00227 }
00228 }
00229
00230
00231 ExplainEndOutput(&es);
00232 Assert(es.indent == 0);
00233
00234
00235 tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt));
00236 if (es.format == EXPLAIN_FORMAT_TEXT)
00237 do_text_output_multiline(tstate, es.str->data);
00238 else
00239 do_text_output_oneline(tstate, es.str->data);
00240 end_tup_output(tstate);
00241
00242 pfree(es.str->data);
00243 }
00244
00245
00246
00247
00248 void
00249 ExplainInitState(ExplainState *es)
00250 {
00251
00252 memset(es, 0, sizeof(ExplainState));
00253 es->costs = true;
00254
00255 es->str = makeStringInfo();
00256 }
00257
00258
00259
00260
00261
00262 TupleDesc
00263 ExplainResultDesc(ExplainStmt *stmt)
00264 {
00265 TupleDesc tupdesc;
00266 ListCell *lc;
00267 Oid result_type = TEXTOID;
00268
00269
00270 foreach(lc, stmt->options)
00271 {
00272 DefElem *opt = (DefElem *) lfirst(lc);
00273
00274 if (strcmp(opt->defname, "format") == 0)
00275 {
00276 char *p = defGetString(opt);
00277
00278 if (strcmp(p, "xml") == 0)
00279 result_type = XMLOID;
00280 else if (strcmp(p, "json") == 0)
00281 result_type = JSONOID;
00282 else
00283 result_type = TEXTOID;
00284
00285 }
00286 }
00287
00288
00289 tupdesc = CreateTemplateTupleDesc(1, false);
00290 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
00291 result_type, -1, 0);
00292 return tupdesc;
00293 }
00294
00295
00296
00297
00298
00299
00300
00301 static void
00302 ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
00303 const char *queryString, ParamListInfo params)
00304 {
00305
00306 if (query->commandType == CMD_UTILITY)
00307 {
00308 ExplainOneUtility(query->utilityStmt, into, es, queryString, params);
00309 return;
00310 }
00311
00312
00313 if (ExplainOneQuery_hook)
00314 (*ExplainOneQuery_hook) (query, into, es, queryString, params);
00315 else
00316 {
00317 PlannedStmt *plan;
00318
00319
00320 plan = pg_plan_query(query, 0, params);
00321
00322
00323 ExplainOnePlan(plan, into, es, queryString, params);
00324 }
00325 }
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338 void
00339 ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
00340 const char *queryString, ParamListInfo params)
00341 {
00342 if (utilityStmt == NULL)
00343 return;
00344
00345 if (IsA(utilityStmt, CreateTableAsStmt))
00346 {
00347
00348
00349
00350
00351
00352 CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
00353 List *rewritten;
00354
00355 Assert(IsA(ctas->query, Query));
00356 rewritten = QueryRewrite((Query *) copyObject(ctas->query));
00357 Assert(list_length(rewritten) == 1);
00358 ExplainOneQuery((Query *) linitial(rewritten), ctas->into, es,
00359 queryString, params);
00360 }
00361 else if (IsA(utilityStmt, ExecuteStmt))
00362 ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
00363 queryString, params);
00364 else if (IsA(utilityStmt, NotifyStmt))
00365 {
00366 if (es->format == EXPLAIN_FORMAT_TEXT)
00367 appendStringInfoString(es->str, "NOTIFY\n");
00368 else
00369 ExplainDummyGroup("Notify", NULL, es);
00370 }
00371 else
00372 {
00373 if (es->format == EXPLAIN_FORMAT_TEXT)
00374 appendStringInfoString(es->str,
00375 "Utility statements have no plan structure\n");
00376 else
00377 ExplainDummyGroup("Utility Statement", NULL, es);
00378 }
00379 }
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398 void
00399 ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
00400 const char *queryString, ParamListInfo params)
00401 {
00402 DestReceiver *dest;
00403 QueryDesc *queryDesc;
00404 instr_time starttime;
00405 double totaltime = 0;
00406 int eflags;
00407 int instrument_option = 0;
00408
00409 if (es->analyze && es->timing)
00410 instrument_option |= INSTRUMENT_TIMER;
00411 else if (es->analyze)
00412 instrument_option |= INSTRUMENT_ROWS;
00413
00414 if (es->buffers)
00415 instrument_option |= INSTRUMENT_BUFFERS;
00416
00417 INSTR_TIME_SET_CURRENT(starttime);
00418
00419
00420
00421
00422
00423 PushCopiedSnapshot(GetActiveSnapshot());
00424 UpdateActiveSnapshotCommandId();
00425
00426
00427
00428
00429
00430 if (into)
00431 dest = CreateIntoRelDestReceiver(into);
00432 else
00433 dest = None_Receiver;
00434
00435
00436 queryDesc = CreateQueryDesc(plannedstmt, queryString,
00437 GetActiveSnapshot(), InvalidSnapshot,
00438 dest, params, instrument_option);
00439
00440
00441 if (es->analyze)
00442 eflags = 0;
00443 else
00444 eflags = EXEC_FLAG_EXPLAIN_ONLY;
00445 if (into)
00446 eflags |= GetIntoRelEFlags(into);
00447
00448
00449 ExecutorStart(queryDesc, eflags);
00450
00451
00452 if (es->analyze)
00453 {
00454 ScanDirection dir;
00455
00456
00457 if (into && into->skipData)
00458 dir = NoMovementScanDirection;
00459 else
00460 dir = ForwardScanDirection;
00461
00462
00463 ExecutorRun(queryDesc, dir, 0L);
00464
00465
00466 ExecutorFinish(queryDesc);
00467
00468
00469 totaltime += elapsed_time(&starttime);
00470 }
00471
00472 ExplainOpenGroup("Query", NULL, true, es);
00473
00474
00475 ExplainPrintPlan(es, queryDesc);
00476
00477
00478 if (es->analyze)
00479 {
00480 ResultRelInfo *rInfo;
00481 bool show_relname;
00482 int numrels = queryDesc->estate->es_num_result_relations;
00483 List *targrels = queryDesc->estate->es_trig_target_relations;
00484 int nr;
00485 ListCell *l;
00486
00487 ExplainOpenGroup("Triggers", "Triggers", false, es);
00488
00489 show_relname = (numrels > 1 || targrels != NIL);
00490 rInfo = queryDesc->estate->es_result_relations;
00491 for (nr = 0; nr < numrels; rInfo++, nr++)
00492 report_triggers(rInfo, show_relname, es);
00493
00494 foreach(l, targrels)
00495 {
00496 rInfo = (ResultRelInfo *) lfirst(l);
00497 report_triggers(rInfo, show_relname, es);
00498 }
00499
00500 ExplainCloseGroup("Triggers", "Triggers", false, es);
00501 }
00502
00503
00504
00505
00506
00507 INSTR_TIME_SET_CURRENT(starttime);
00508
00509 ExecutorEnd(queryDesc);
00510
00511 FreeQueryDesc(queryDesc);
00512
00513 PopActiveSnapshot();
00514
00515
00516 if (es->analyze)
00517 CommandCounterIncrement();
00518
00519 totaltime += elapsed_time(&starttime);
00520
00521 if (es->analyze)
00522 {
00523 if (es->format == EXPLAIN_FORMAT_TEXT)
00524 appendStringInfo(es->str, "Total runtime: %.3f ms\n",
00525 1000.0 * totaltime);
00526 else
00527 ExplainPropertyFloat("Total Runtime", 1000.0 * totaltime,
00528 3, es);
00529 }
00530
00531 ExplainCloseGroup("Query", NULL, true, es);
00532 }
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544 void
00545 ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
00546 {
00547 Bitmapset *rels_used = NULL;
00548
00549 Assert(queryDesc->plannedstmt != NULL);
00550 es->pstmt = queryDesc->plannedstmt;
00551 es->rtable = queryDesc->plannedstmt->rtable;
00552 ExplainPreScanNode(queryDesc->planstate, &rels_used);
00553 es->rtable_names = select_rtable_names_for_explain(es->rtable, rels_used);
00554 ExplainNode(queryDesc->planstate, NIL, NULL, NULL, es);
00555 }
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565 void
00566 ExplainQueryText(ExplainState *es, QueryDesc *queryDesc)
00567 {
00568 if (queryDesc->sourceText)
00569 ExplainPropertyText("Query Text", queryDesc->sourceText, es);
00570 }
00571
00572
00573
00574
00575
00576 static void
00577 report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
00578 {
00579 int nt;
00580
00581 if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
00582 return;
00583 for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
00584 {
00585 Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
00586 Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
00587 char *relname;
00588 char *conname = NULL;
00589
00590
00591 InstrEndLoop(instr);
00592
00593
00594
00595
00596
00597 if (instr->ntuples == 0)
00598 continue;
00599
00600 ExplainOpenGroup("Trigger", NULL, true, es);
00601
00602 relname = RelationGetRelationName(rInfo->ri_RelationDesc);
00603 if (OidIsValid(trig->tgconstraint))
00604 conname = get_constraint_name(trig->tgconstraint);
00605
00606
00607
00608
00609
00610
00611 if (es->format == EXPLAIN_FORMAT_TEXT)
00612 {
00613 if (es->verbose || conname == NULL)
00614 appendStringInfo(es->str, "Trigger %s", trig->tgname);
00615 else
00616 appendStringInfoString(es->str, "Trigger");
00617 if (conname)
00618 appendStringInfo(es->str, " for constraint %s", conname);
00619 if (show_relname)
00620 appendStringInfo(es->str, " on %s", relname);
00621 appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
00622 1000.0 * instr->total, instr->ntuples);
00623 }
00624 else
00625 {
00626 ExplainPropertyText("Trigger Name", trig->tgname, es);
00627 if (conname)
00628 ExplainPropertyText("Constraint Name", conname, es);
00629 ExplainPropertyText("Relation", relname, es);
00630 ExplainPropertyFloat("Time", 1000.0 * instr->total, 3, es);
00631 ExplainPropertyFloat("Calls", instr->ntuples, 0, es);
00632 }
00633
00634 if (conname)
00635 pfree(conname);
00636
00637 ExplainCloseGroup("Trigger", NULL, true, es);
00638 }
00639 }
00640
00641
00642 static double
00643 elapsed_time(instr_time *starttime)
00644 {
00645 instr_time endtime;
00646
00647 INSTR_TIME_SET_CURRENT(endtime);
00648 INSTR_TIME_SUBTRACT(endtime, *starttime);
00649 return INSTR_TIME_GET_DOUBLE(endtime);
00650 }
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661 static void
00662 ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
00663 {
00664 Plan *plan = planstate->plan;
00665
00666 switch (nodeTag(plan))
00667 {
00668 case T_SeqScan:
00669 case T_IndexScan:
00670 case T_IndexOnlyScan:
00671 case T_BitmapHeapScan:
00672 case T_TidScan:
00673 case T_SubqueryScan:
00674 case T_FunctionScan:
00675 case T_ValuesScan:
00676 case T_CteScan:
00677 case T_WorkTableScan:
00678 case T_ForeignScan:
00679 *rels_used = bms_add_member(*rels_used,
00680 ((Scan *) plan)->scanrelid);
00681 break;
00682 case T_ModifyTable:
00683
00684 *rels_used = bms_add_member(*rels_used,
00685 linitial_int(((ModifyTable *) plan)->resultRelations));
00686 break;
00687 default:
00688 break;
00689 }
00690
00691
00692 if (planstate->initPlan)
00693 ExplainPreScanSubPlans(planstate->initPlan, rels_used);
00694
00695
00696 if (outerPlanState(planstate))
00697 ExplainPreScanNode(outerPlanState(planstate), rels_used);
00698
00699
00700 if (innerPlanState(planstate))
00701 ExplainPreScanNode(innerPlanState(planstate), rels_used);
00702
00703
00704 switch (nodeTag(plan))
00705 {
00706 case T_ModifyTable:
00707 ExplainPreScanMemberNodes(((ModifyTable *) plan)->plans,
00708 ((ModifyTableState *) planstate)->mt_plans,
00709 rels_used);
00710 break;
00711 case T_Append:
00712 ExplainPreScanMemberNodes(((Append *) plan)->appendplans,
00713 ((AppendState *) planstate)->appendplans,
00714 rels_used);
00715 break;
00716 case T_MergeAppend:
00717 ExplainPreScanMemberNodes(((MergeAppend *) plan)->mergeplans,
00718 ((MergeAppendState *) planstate)->mergeplans,
00719 rels_used);
00720 break;
00721 case T_BitmapAnd:
00722 ExplainPreScanMemberNodes(((BitmapAnd *) plan)->bitmapplans,
00723 ((BitmapAndState *) planstate)->bitmapplans,
00724 rels_used);
00725 break;
00726 case T_BitmapOr:
00727 ExplainPreScanMemberNodes(((BitmapOr *) plan)->bitmapplans,
00728 ((BitmapOrState *) planstate)->bitmapplans,
00729 rels_used);
00730 break;
00731 case T_SubqueryScan:
00732 ExplainPreScanNode(((SubqueryScanState *) planstate)->subplan,
00733 rels_used);
00734 break;
00735 default:
00736 break;
00737 }
00738
00739
00740 if (planstate->subPlan)
00741 ExplainPreScanSubPlans(planstate->subPlan, rels_used);
00742 }
00743
00744
00745
00746
00747
00748
00749
00750
00751 static void
00752 ExplainPreScanMemberNodes(List *plans, PlanState **planstates,
00753 Bitmapset **rels_used)
00754 {
00755 int nplans = list_length(plans);
00756 int j;
00757
00758 for (j = 0; j < nplans; j++)
00759 ExplainPreScanNode(planstates[j], rels_used);
00760 }
00761
00762
00763
00764
00765 static void
00766 ExplainPreScanSubPlans(List *plans, Bitmapset **rels_used)
00767 {
00768 ListCell *lst;
00769
00770 foreach(lst, plans)
00771 {
00772 SubPlanState *sps = (SubPlanState *) lfirst(lst);
00773
00774 ExplainPreScanNode(sps->planstate, rels_used);
00775 }
00776 }
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798 static void
00799 ExplainNode(PlanState *planstate, List *ancestors,
00800 const char *relationship, const char *plan_name,
00801 ExplainState *es)
00802 {
00803 Plan *plan = planstate->plan;
00804 const char *pname;
00805 const char *sname;
00806 const char *strategy = NULL;
00807 const char *operation = NULL;
00808 int save_indent = es->indent;
00809 bool haschildren;
00810
00811 switch (nodeTag(plan))
00812 {
00813 case T_Result:
00814 pname = sname = "Result";
00815 break;
00816 case T_ModifyTable:
00817 sname = "ModifyTable";
00818 switch (((ModifyTable *) plan)->operation)
00819 {
00820 case CMD_INSERT:
00821 pname = operation = "Insert";
00822 break;
00823 case CMD_UPDATE:
00824 pname = operation = "Update";
00825 break;
00826 case CMD_DELETE:
00827 pname = operation = "Delete";
00828 break;
00829 default:
00830 pname = "???";
00831 break;
00832 }
00833 break;
00834 case T_Append:
00835 pname = sname = "Append";
00836 break;
00837 case T_MergeAppend:
00838 pname = sname = "Merge Append";
00839 break;
00840 case T_RecursiveUnion:
00841 pname = sname = "Recursive Union";
00842 break;
00843 case T_BitmapAnd:
00844 pname = sname = "BitmapAnd";
00845 break;
00846 case T_BitmapOr:
00847 pname = sname = "BitmapOr";
00848 break;
00849 case T_NestLoop:
00850 pname = sname = "Nested Loop";
00851 break;
00852 case T_MergeJoin:
00853 pname = "Merge";
00854 sname = "Merge Join";
00855 break;
00856 case T_HashJoin:
00857 pname = "Hash";
00858 sname = "Hash Join";
00859 break;
00860 case T_SeqScan:
00861 pname = sname = "Seq Scan";
00862 break;
00863 case T_IndexScan:
00864 pname = sname = "Index Scan";
00865 break;
00866 case T_IndexOnlyScan:
00867 pname = sname = "Index Only Scan";
00868 break;
00869 case T_BitmapIndexScan:
00870 pname = sname = "Bitmap Index Scan";
00871 break;
00872 case T_BitmapHeapScan:
00873 pname = sname = "Bitmap Heap Scan";
00874 break;
00875 case T_TidScan:
00876 pname = sname = "Tid Scan";
00877 break;
00878 case T_SubqueryScan:
00879 pname = sname = "Subquery Scan";
00880 break;
00881 case T_FunctionScan:
00882 pname = sname = "Function Scan";
00883 break;
00884 case T_ValuesScan:
00885 pname = sname = "Values Scan";
00886 break;
00887 case T_CteScan:
00888 pname = sname = "CTE Scan";
00889 break;
00890 case T_WorkTableScan:
00891 pname = sname = "WorkTable Scan";
00892 break;
00893 case T_ForeignScan:
00894 pname = sname = "Foreign Scan";
00895 break;
00896 case T_Material:
00897 pname = sname = "Materialize";
00898 break;
00899 case T_Sort:
00900 pname = sname = "Sort";
00901 break;
00902 case T_Group:
00903 pname = sname = "Group";
00904 break;
00905 case T_Agg:
00906 sname = "Aggregate";
00907 switch (((Agg *) plan)->aggstrategy)
00908 {
00909 case AGG_PLAIN:
00910 pname = "Aggregate";
00911 strategy = "Plain";
00912 break;
00913 case AGG_SORTED:
00914 pname = "GroupAggregate";
00915 strategy = "Sorted";
00916 break;
00917 case AGG_HASHED:
00918 pname = "HashAggregate";
00919 strategy = "Hashed";
00920 break;
00921 default:
00922 pname = "Aggregate ???";
00923 strategy = "???";
00924 break;
00925 }
00926 break;
00927 case T_WindowAgg:
00928 pname = sname = "WindowAgg";
00929 break;
00930 case T_Unique:
00931 pname = sname = "Unique";
00932 break;
00933 case T_SetOp:
00934 sname = "SetOp";
00935 switch (((SetOp *) plan)->strategy)
00936 {
00937 case SETOP_SORTED:
00938 pname = "SetOp";
00939 strategy = "Sorted";
00940 break;
00941 case SETOP_HASHED:
00942 pname = "HashSetOp";
00943 strategy = "Hashed";
00944 break;
00945 default:
00946 pname = "SetOp ???";
00947 strategy = "???";
00948 break;
00949 }
00950 break;
00951 case T_LockRows:
00952 pname = sname = "LockRows";
00953 break;
00954 case T_Limit:
00955 pname = sname = "Limit";
00956 break;
00957 case T_Hash:
00958 pname = sname = "Hash";
00959 break;
00960 default:
00961 pname = sname = "???";
00962 break;
00963 }
00964
00965 ExplainOpenGroup("Plan",
00966 relationship ? NULL : "Plan",
00967 true, es);
00968
00969 if (es->format == EXPLAIN_FORMAT_TEXT)
00970 {
00971 if (plan_name)
00972 {
00973 appendStringInfoSpaces(es->str, es->indent * 2);
00974 appendStringInfo(es->str, "%s\n", plan_name);
00975 es->indent++;
00976 }
00977 if (es->indent)
00978 {
00979 appendStringInfoSpaces(es->str, es->indent * 2);
00980 appendStringInfoString(es->str, "-> ");
00981 es->indent += 2;
00982 }
00983 appendStringInfoString(es->str, pname);
00984 es->indent++;
00985 }
00986 else
00987 {
00988 ExplainPropertyText("Node Type", sname, es);
00989 if (strategy)
00990 ExplainPropertyText("Strategy", strategy, es);
00991 if (operation)
00992 ExplainPropertyText("Operation", operation, es);
00993 if (relationship)
00994 ExplainPropertyText("Parent Relationship", relationship, es);
00995 if (plan_name)
00996 ExplainPropertyText("Subplan Name", plan_name, es);
00997 }
00998
00999 switch (nodeTag(plan))
01000 {
01001 case T_SeqScan:
01002 case T_BitmapHeapScan:
01003 case T_TidScan:
01004 case T_SubqueryScan:
01005 case T_FunctionScan:
01006 case T_ValuesScan:
01007 case T_CteScan:
01008 case T_WorkTableScan:
01009 case T_ForeignScan:
01010 ExplainScanTarget((Scan *) plan, es);
01011 break;
01012 case T_IndexScan:
01013 {
01014 IndexScan *indexscan = (IndexScan *) plan;
01015
01016 ExplainIndexScanDetails(indexscan->indexid,
01017 indexscan->indexorderdir,
01018 es);
01019 ExplainScanTarget((Scan *) indexscan, es);
01020 }
01021 break;
01022 case T_IndexOnlyScan:
01023 {
01024 IndexOnlyScan *indexonlyscan = (IndexOnlyScan *) plan;
01025
01026 ExplainIndexScanDetails(indexonlyscan->indexid,
01027 indexonlyscan->indexorderdir,
01028 es);
01029 ExplainScanTarget((Scan *) indexonlyscan, es);
01030 }
01031 break;
01032 case T_BitmapIndexScan:
01033 {
01034 BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
01035 const char *indexname =
01036 explain_get_index_name(bitmapindexscan->indexid);
01037
01038 if (es->format == EXPLAIN_FORMAT_TEXT)
01039 appendStringInfo(es->str, " on %s", indexname);
01040 else
01041 ExplainPropertyText("Index Name", indexname, es);
01042 }
01043 break;
01044 case T_ModifyTable:
01045 ExplainModifyTarget((ModifyTable *) plan, es);
01046 break;
01047 case T_NestLoop:
01048 case T_MergeJoin:
01049 case T_HashJoin:
01050 {
01051 const char *jointype;
01052
01053 switch (((Join *) plan)->jointype)
01054 {
01055 case JOIN_INNER:
01056 jointype = "Inner";
01057 break;
01058 case JOIN_LEFT:
01059 jointype = "Left";
01060 break;
01061 case JOIN_FULL:
01062 jointype = "Full";
01063 break;
01064 case JOIN_RIGHT:
01065 jointype = "Right";
01066 break;
01067 case JOIN_SEMI:
01068 jointype = "Semi";
01069 break;
01070 case JOIN_ANTI:
01071 jointype = "Anti";
01072 break;
01073 default:
01074 jointype = "???";
01075 break;
01076 }
01077 if (es->format == EXPLAIN_FORMAT_TEXT)
01078 {
01079
01080
01081
01082
01083 if (((Join *) plan)->jointype != JOIN_INNER)
01084 appendStringInfo(es->str, " %s Join", jointype);
01085 else if (!IsA(plan, NestLoop))
01086 appendStringInfo(es->str, " Join");
01087 }
01088 else
01089 ExplainPropertyText("Join Type", jointype, es);
01090 }
01091 break;
01092 case T_SetOp:
01093 {
01094 const char *setopcmd;
01095
01096 switch (((SetOp *) plan)->cmd)
01097 {
01098 case SETOPCMD_INTERSECT:
01099 setopcmd = "Intersect";
01100 break;
01101 case SETOPCMD_INTERSECT_ALL:
01102 setopcmd = "Intersect All";
01103 break;
01104 case SETOPCMD_EXCEPT:
01105 setopcmd = "Except";
01106 break;
01107 case SETOPCMD_EXCEPT_ALL:
01108 setopcmd = "Except All";
01109 break;
01110 default:
01111 setopcmd = "???";
01112 break;
01113 }
01114 if (es->format == EXPLAIN_FORMAT_TEXT)
01115 appendStringInfo(es->str, " %s", setopcmd);
01116 else
01117 ExplainPropertyText("Command", setopcmd, es);
01118 }
01119 break;
01120 default:
01121 break;
01122 }
01123
01124 if (es->costs)
01125 {
01126 if (es->format == EXPLAIN_FORMAT_TEXT)
01127 {
01128 appendStringInfo(es->str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
01129 plan->startup_cost, plan->total_cost,
01130 plan->plan_rows, plan->plan_width);
01131 }
01132 else
01133 {
01134 ExplainPropertyFloat("Startup Cost", plan->startup_cost, 2, es);
01135 ExplainPropertyFloat("Total Cost", plan->total_cost, 2, es);
01136 ExplainPropertyFloat("Plan Rows", plan->plan_rows, 0, es);
01137 ExplainPropertyInteger("Plan Width", plan->plan_width, es);
01138 }
01139 }
01140
01141
01142
01143
01144
01145 if (planstate->instrument)
01146 InstrEndLoop(planstate->instrument);
01147
01148 if (planstate->instrument && planstate->instrument->nloops > 0)
01149 {
01150 double nloops = planstate->instrument->nloops;
01151 double startup_sec = 1000.0 * planstate->instrument->startup / nloops;
01152 double total_sec = 1000.0 * planstate->instrument->total / nloops;
01153 double rows = planstate->instrument->ntuples / nloops;
01154
01155 if (es->format == EXPLAIN_FORMAT_TEXT)
01156 {
01157 if (planstate->instrument->need_timer)
01158 appendStringInfo(es->str,
01159 " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
01160 startup_sec, total_sec, rows, nloops);
01161 else
01162 appendStringInfo(es->str,
01163 " (actual rows=%.0f loops=%.0f)",
01164 rows, nloops);
01165 }
01166 else
01167 {
01168 if (planstate->instrument->need_timer)
01169 {
01170 ExplainPropertyFloat("Actual Startup Time", startup_sec, 3, es);
01171 ExplainPropertyFloat("Actual Total Time", total_sec, 3, es);
01172 }
01173 ExplainPropertyFloat("Actual Rows", rows, 0, es);
01174 ExplainPropertyFloat("Actual Loops", nloops, 0, es);
01175 }
01176 }
01177 else if (es->analyze)
01178 {
01179
01180 if (es->format == EXPLAIN_FORMAT_TEXT)
01181 appendStringInfo(es->str, " (never executed)");
01182 else if (planstate->instrument->need_timer)
01183 {
01184 ExplainPropertyFloat("Actual Startup Time", 0.0, 3, es);
01185 ExplainPropertyFloat("Actual Total Time", 0.0, 3, es);
01186 }
01187 else
01188 {
01189 ExplainPropertyFloat("Actual Rows", 0.0, 0, es);
01190 ExplainPropertyFloat("Actual Loops", 0.0, 0, es);
01191 }
01192
01193 }
01194
01195
01196 if (es->format == EXPLAIN_FORMAT_TEXT)
01197 appendStringInfoChar(es->str, '\n');
01198
01199
01200 if (es->verbose)
01201 show_plan_tlist(planstate, ancestors, es);
01202
01203
01204 switch (nodeTag(plan))
01205 {
01206 case T_IndexScan:
01207 show_scan_qual(((IndexScan *) plan)->indexqualorig,
01208 "Index Cond", planstate, ancestors, es);
01209 if (((IndexScan *) plan)->indexqualorig)
01210 show_instrumentation_count("Rows Removed by Index Recheck", 2,
01211 planstate, es);
01212 show_scan_qual(((IndexScan *) plan)->indexorderbyorig,
01213 "Order By", planstate, ancestors, es);
01214 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
01215 if (plan->qual)
01216 show_instrumentation_count("Rows Removed by Filter", 1,
01217 planstate, es);
01218 break;
01219 case T_IndexOnlyScan:
01220 show_scan_qual(((IndexOnlyScan *) plan)->indexqual,
01221 "Index Cond", planstate, ancestors, es);
01222 if (((IndexOnlyScan *) plan)->indexqual)
01223 show_instrumentation_count("Rows Removed by Index Recheck", 2,
01224 planstate, es);
01225 show_scan_qual(((IndexOnlyScan *) plan)->indexorderby,
01226 "Order By", planstate, ancestors, es);
01227 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
01228 if (plan->qual)
01229 show_instrumentation_count("Rows Removed by Filter", 1,
01230 planstate, es);
01231 if (es->analyze)
01232 ExplainPropertyLong("Heap Fetches",
01233 ((IndexOnlyScanState *) planstate)->ioss_HeapFetches, es);
01234 break;
01235 case T_BitmapIndexScan:
01236 show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
01237 "Index Cond", planstate, ancestors, es);
01238 break;
01239 case T_BitmapHeapScan:
01240 show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
01241 "Recheck Cond", planstate, ancestors, es);
01242 if (((BitmapHeapScan *) plan)->bitmapqualorig)
01243 show_instrumentation_count("Rows Removed by Index Recheck", 2,
01244 planstate, es);
01245
01246 case T_SeqScan:
01247 case T_ValuesScan:
01248 case T_CteScan:
01249 case T_WorkTableScan:
01250 case T_SubqueryScan:
01251 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
01252 if (plan->qual)
01253 show_instrumentation_count("Rows Removed by Filter", 1,
01254 planstate, es);
01255 break;
01256 case T_FunctionScan:
01257 if (es->verbose)
01258 show_expression(((FunctionScan *) plan)->funcexpr,
01259 "Function Call", planstate, ancestors,
01260 es->verbose, es);
01261 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
01262 if (plan->qual)
01263 show_instrumentation_count("Rows Removed by Filter", 1,
01264 planstate, es);
01265 break;
01266 case T_TidScan:
01267 {
01268
01269
01270
01271
01272 List *tidquals = ((TidScan *) plan)->tidquals;
01273
01274 if (list_length(tidquals) > 1)
01275 tidquals = list_make1(make_orclause(tidquals));
01276 show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
01277 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
01278 if (plan->qual)
01279 show_instrumentation_count("Rows Removed by Filter", 1,
01280 planstate, es);
01281 }
01282 break;
01283 case T_ForeignScan:
01284 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
01285 if (plan->qual)
01286 show_instrumentation_count("Rows Removed by Filter", 1,
01287 planstate, es);
01288 show_foreignscan_info((ForeignScanState *) planstate, es);
01289 break;
01290 case T_NestLoop:
01291 show_upper_qual(((NestLoop *) plan)->join.joinqual,
01292 "Join Filter", planstate, ancestors, es);
01293 if (((NestLoop *) plan)->join.joinqual)
01294 show_instrumentation_count("Rows Removed by Join Filter", 1,
01295 planstate, es);
01296 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
01297 if (plan->qual)
01298 show_instrumentation_count("Rows Removed by Filter", 2,
01299 planstate, es);
01300 break;
01301 case T_MergeJoin:
01302 show_upper_qual(((MergeJoin *) plan)->mergeclauses,
01303 "Merge Cond", planstate, ancestors, es);
01304 show_upper_qual(((MergeJoin *) plan)->join.joinqual,
01305 "Join Filter", planstate, ancestors, es);
01306 if (((MergeJoin *) plan)->join.joinqual)
01307 show_instrumentation_count("Rows Removed by Join Filter", 1,
01308 planstate, es);
01309 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
01310 if (plan->qual)
01311 show_instrumentation_count("Rows Removed by Filter", 2,
01312 planstate, es);
01313 break;
01314 case T_HashJoin:
01315 show_upper_qual(((HashJoin *) plan)->hashclauses,
01316 "Hash Cond", planstate, ancestors, es);
01317 show_upper_qual(((HashJoin *) plan)->join.joinqual,
01318 "Join Filter", planstate, ancestors, es);
01319 if (((HashJoin *) plan)->join.joinqual)
01320 show_instrumentation_count("Rows Removed by Join Filter", 1,
01321 planstate, es);
01322 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
01323 if (plan->qual)
01324 show_instrumentation_count("Rows Removed by Filter", 2,
01325 planstate, es);
01326 break;
01327 case T_Agg:
01328 case T_Group:
01329 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
01330 if (plan->qual)
01331 show_instrumentation_count("Rows Removed by Filter", 1,
01332 planstate, es);
01333 break;
01334 case T_Sort:
01335 show_sort_keys((SortState *) planstate, ancestors, es);
01336 show_sort_info((SortState *) planstate, es);
01337 break;
01338 case T_MergeAppend:
01339 show_merge_append_keys((MergeAppendState *) planstate,
01340 ancestors, es);
01341 break;
01342 case T_Result:
01343 show_upper_qual((List *) ((Result *) plan)->resconstantqual,
01344 "One-Time Filter", planstate, ancestors, es);
01345 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
01346 if (plan->qual)
01347 show_instrumentation_count("Rows Removed by Filter", 1,
01348 planstate, es);
01349 break;
01350 case T_ModifyTable:
01351 show_modifytable_info((ModifyTableState *) planstate, es);
01352 break;
01353 case T_Hash:
01354 show_hash_info((HashState *) planstate, es);
01355 break;
01356 default:
01357 break;
01358 }
01359
01360
01361 if (es->buffers)
01362 {
01363 const BufferUsage *usage = &planstate->instrument->bufusage;
01364
01365 if (es->format == EXPLAIN_FORMAT_TEXT)
01366 {
01367 bool has_shared = (usage->shared_blks_hit > 0 ||
01368 usage->shared_blks_read > 0 ||
01369 usage->shared_blks_dirtied > 0 ||
01370 usage->shared_blks_written > 0);
01371 bool has_local = (usage->local_blks_hit > 0 ||
01372 usage->local_blks_read > 0 ||
01373 usage->local_blks_dirtied > 0 ||
01374 usage->local_blks_written > 0);
01375 bool has_temp = (usage->temp_blks_read > 0 ||
01376 usage->temp_blks_written > 0);
01377 bool has_timing = (!INSTR_TIME_IS_ZERO(usage->blk_read_time) ||
01378 !INSTR_TIME_IS_ZERO(usage->blk_write_time));
01379
01380
01381 if (has_shared || has_local || has_temp)
01382 {
01383 appendStringInfoSpaces(es->str, es->indent * 2);
01384 appendStringInfoString(es->str, "Buffers:");
01385
01386 if (has_shared)
01387 {
01388 appendStringInfoString(es->str, " shared");
01389 if (usage->shared_blks_hit > 0)
01390 appendStringInfo(es->str, " hit=%ld",
01391 usage->shared_blks_hit);
01392 if (usage->shared_blks_read > 0)
01393 appendStringInfo(es->str, " read=%ld",
01394 usage->shared_blks_read);
01395 if (usage->shared_blks_dirtied > 0)
01396 appendStringInfo(es->str, " dirtied=%ld",
01397 usage->shared_blks_dirtied);
01398 if (usage->shared_blks_written > 0)
01399 appendStringInfo(es->str, " written=%ld",
01400 usage->shared_blks_written);
01401 if (has_local || has_temp)
01402 appendStringInfoChar(es->str, ',');
01403 }
01404 if (has_local)
01405 {
01406 appendStringInfoString(es->str, " local");
01407 if (usage->local_blks_hit > 0)
01408 appendStringInfo(es->str, " hit=%ld",
01409 usage->local_blks_hit);
01410 if (usage->local_blks_read > 0)
01411 appendStringInfo(es->str, " read=%ld",
01412 usage->local_blks_read);
01413 if (usage->local_blks_dirtied > 0)
01414 appendStringInfo(es->str, " dirtied=%ld",
01415 usage->local_blks_dirtied);
01416 if (usage->local_blks_written > 0)
01417 appendStringInfo(es->str, " written=%ld",
01418 usage->local_blks_written);
01419 if (has_temp)
01420 appendStringInfoChar(es->str, ',');
01421 }
01422 if (has_temp)
01423 {
01424 appendStringInfoString(es->str, " temp");
01425 if (usage->temp_blks_read > 0)
01426 appendStringInfo(es->str, " read=%ld",
01427 usage->temp_blks_read);
01428 if (usage->temp_blks_written > 0)
01429 appendStringInfo(es->str, " written=%ld",
01430 usage->temp_blks_written);
01431 }
01432 appendStringInfoChar(es->str, '\n');
01433 }
01434
01435
01436 if (has_timing)
01437 {
01438 appendStringInfoSpaces(es->str, es->indent * 2);
01439 appendStringInfoString(es->str, "I/O Timings:");
01440 if (!INSTR_TIME_IS_ZERO(usage->blk_read_time))
01441 appendStringInfo(es->str, " read=%0.3f",
01442 INSTR_TIME_GET_MILLISEC(usage->blk_read_time));
01443 if (!INSTR_TIME_IS_ZERO(usage->blk_write_time))
01444 appendStringInfo(es->str, " write=%0.3f",
01445 INSTR_TIME_GET_MILLISEC(usage->blk_write_time));
01446 appendStringInfoChar(es->str, '\n');
01447 }
01448 }
01449 else
01450 {
01451 ExplainPropertyLong("Shared Hit Blocks", usage->shared_blks_hit, es);
01452 ExplainPropertyLong("Shared Read Blocks", usage->shared_blks_read, es);
01453 ExplainPropertyLong("Shared Dirtied Blocks", usage->shared_blks_dirtied, es);
01454 ExplainPropertyLong("Shared Written Blocks", usage->shared_blks_written, es);
01455 ExplainPropertyLong("Local Hit Blocks", usage->local_blks_hit, es);
01456 ExplainPropertyLong("Local Read Blocks", usage->local_blks_read, es);
01457 ExplainPropertyLong("Local Dirtied Blocks", usage->local_blks_dirtied, es);
01458 ExplainPropertyLong("Local Written Blocks", usage->local_blks_written, es);
01459 ExplainPropertyLong("Temp Read Blocks", usage->temp_blks_read, es);
01460 ExplainPropertyLong("Temp Written Blocks", usage->temp_blks_written, es);
01461 ExplainPropertyFloat("I/O Read Time", INSTR_TIME_GET_MILLISEC(usage->blk_read_time), 3, es);
01462 ExplainPropertyFloat("I/O Write Time", INSTR_TIME_GET_MILLISEC(usage->blk_write_time), 3, es);
01463 }
01464 }
01465
01466
01467 haschildren = planstate->initPlan ||
01468 outerPlanState(planstate) ||
01469 innerPlanState(planstate) ||
01470 IsA(plan, ModifyTable) ||
01471 IsA(plan, Append) ||
01472 IsA(plan, MergeAppend) ||
01473 IsA(plan, BitmapAnd) ||
01474 IsA(plan, BitmapOr) ||
01475 IsA(plan, SubqueryScan) ||
01476 planstate->subPlan;
01477 if (haschildren)
01478 {
01479 ExplainOpenGroup("Plans", "Plans", false, es);
01480
01481 ancestors = lcons(planstate, ancestors);
01482 }
01483
01484
01485 if (planstate->initPlan)
01486 ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan", es);
01487
01488
01489 if (outerPlanState(planstate))
01490 ExplainNode(outerPlanState(planstate), ancestors,
01491 "Outer", NULL, es);
01492
01493
01494 if (innerPlanState(planstate))
01495 ExplainNode(innerPlanState(planstate), ancestors,
01496 "Inner", NULL, es);
01497
01498
01499 switch (nodeTag(plan))
01500 {
01501 case T_ModifyTable:
01502 ExplainMemberNodes(((ModifyTable *) plan)->plans,
01503 ((ModifyTableState *) planstate)->mt_plans,
01504 ancestors, es);
01505 break;
01506 case T_Append:
01507 ExplainMemberNodes(((Append *) plan)->appendplans,
01508 ((AppendState *) planstate)->appendplans,
01509 ancestors, es);
01510 break;
01511 case T_MergeAppend:
01512 ExplainMemberNodes(((MergeAppend *) plan)->mergeplans,
01513 ((MergeAppendState *) planstate)->mergeplans,
01514 ancestors, es);
01515 break;
01516 case T_BitmapAnd:
01517 ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
01518 ((BitmapAndState *) planstate)->bitmapplans,
01519 ancestors, es);
01520 break;
01521 case T_BitmapOr:
01522 ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
01523 ((BitmapOrState *) planstate)->bitmapplans,
01524 ancestors, es);
01525 break;
01526 case T_SubqueryScan:
01527 ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
01528 "Subquery", NULL, es);
01529 break;
01530 default:
01531 break;
01532 }
01533
01534
01535 if (planstate->subPlan)
01536 ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);
01537
01538
01539 if (haschildren)
01540 {
01541 ancestors = list_delete_first(ancestors);
01542 ExplainCloseGroup("Plans", "Plans", false, es);
01543 }
01544
01545
01546 if (es->format == EXPLAIN_FORMAT_TEXT)
01547 es->indent = save_indent;
01548
01549 ExplainCloseGroup("Plan",
01550 relationship ? NULL : "Plan",
01551 true, es);
01552 }
01553
01554
01555
01556
01557 static void
01558 show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
01559 {
01560 Plan *plan = planstate->plan;
01561 List *context;
01562 List *result = NIL;
01563 bool useprefix;
01564 ListCell *lc;
01565
01566
01567 if (plan->targetlist == NIL)
01568 return;
01569
01570 if (IsA(plan, Append))
01571 return;
01572
01573 if (IsA(plan, MergeAppend))
01574 return;
01575 if (IsA(plan, RecursiveUnion))
01576 return;
01577
01578
01579 context = deparse_context_for_planstate((Node *) planstate,
01580 ancestors,
01581 es->rtable,
01582 es->rtable_names);
01583 useprefix = list_length(es->rtable) > 1;
01584
01585
01586 foreach(lc, plan->targetlist)
01587 {
01588 TargetEntry *tle = (TargetEntry *) lfirst(lc);
01589
01590 result = lappend(result,
01591 deparse_expression((Node *) tle->expr, context,
01592 useprefix, false));
01593 }
01594
01595
01596 ExplainPropertyList("Output", result, es);
01597 }
01598
01599
01600
01601
01602 static void
01603 show_expression(Node *node, const char *qlabel,
01604 PlanState *planstate, List *ancestors,
01605 bool useprefix, ExplainState *es)
01606 {
01607 List *context;
01608 char *exprstr;
01609
01610
01611 context = deparse_context_for_planstate((Node *) planstate,
01612 ancestors,
01613 es->rtable,
01614 es->rtable_names);
01615
01616
01617 exprstr = deparse_expression(node, context, useprefix, false);
01618
01619
01620 ExplainPropertyText(qlabel, exprstr, es);
01621 }
01622
01623
01624
01625
01626 static void
01627 show_qual(List *qual, const char *qlabel,
01628 PlanState *planstate, List *ancestors,
01629 bool useprefix, ExplainState *es)
01630 {
01631 Node *node;
01632
01633
01634 if (qual == NIL)
01635 return;
01636
01637
01638 node = (Node *) make_ands_explicit(qual);
01639
01640
01641 show_expression(node, qlabel, planstate, ancestors, useprefix, es);
01642 }
01643
01644
01645
01646
01647 static void
01648 show_scan_qual(List *qual, const char *qlabel,
01649 PlanState *planstate, List *ancestors,
01650 ExplainState *es)
01651 {
01652 bool useprefix;
01653
01654 useprefix = (IsA(planstate->plan, SubqueryScan) ||es->verbose);
01655 show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
01656 }
01657
01658
01659
01660
01661 static void
01662 show_upper_qual(List *qual, const char *qlabel,
01663 PlanState *planstate, List *ancestors,
01664 ExplainState *es)
01665 {
01666 bool useprefix;
01667
01668 useprefix = (list_length(es->rtable) > 1 || es->verbose);
01669 show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
01670 }
01671
01672
01673
01674
01675 static void
01676 show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
01677 {
01678 Sort *plan = (Sort *) sortstate->ss.ps.plan;
01679
01680 show_sort_keys_common((PlanState *) sortstate,
01681 plan->numCols, plan->sortColIdx,
01682 ancestors, es);
01683 }
01684
01685
01686
01687
01688 static void
01689 show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
01690 ExplainState *es)
01691 {
01692 MergeAppend *plan = (MergeAppend *) mstate->ps.plan;
01693
01694 show_sort_keys_common((PlanState *) mstate,
01695 plan->numCols, plan->sortColIdx,
01696 ancestors, es);
01697 }
01698
01699 static void
01700 show_sort_keys_common(PlanState *planstate, int nkeys, AttrNumber *keycols,
01701 List *ancestors, ExplainState *es)
01702 {
01703 Plan *plan = planstate->plan;
01704 List *context;
01705 List *result = NIL;
01706 bool useprefix;
01707 int keyno;
01708 char *exprstr;
01709
01710 if (nkeys <= 0)
01711 return;
01712
01713
01714 context = deparse_context_for_planstate((Node *) planstate,
01715 ancestors,
01716 es->rtable,
01717 es->rtable_names);
01718 useprefix = (list_length(es->rtable) > 1 || es->verbose);
01719
01720 for (keyno = 0; keyno < nkeys; keyno++)
01721 {
01722
01723 AttrNumber keyresno = keycols[keyno];
01724 TargetEntry *target = get_tle_by_resno(plan->targetlist,
01725 keyresno);
01726
01727 if (!target)
01728 elog(ERROR, "no tlist entry for key %d", keyresno);
01729
01730 exprstr = deparse_expression((Node *) target->expr, context,
01731 useprefix, true);
01732 result = lappend(result, exprstr);
01733 }
01734
01735 ExplainPropertyList("Sort Key", result, es);
01736 }
01737
01738
01739
01740
01741 static void
01742 show_sort_info(SortState *sortstate, ExplainState *es)
01743 {
01744 Assert(IsA(sortstate, SortState));
01745 if (es->analyze && sortstate->sort_Done &&
01746 sortstate->tuplesortstate != NULL)
01747 {
01748 Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate;
01749 const char *sortMethod;
01750 const char *spaceType;
01751 long spaceUsed;
01752
01753 tuplesort_get_stats(state, &sortMethod, &spaceType, &spaceUsed);
01754
01755 if (es->format == EXPLAIN_FORMAT_TEXT)
01756 {
01757 appendStringInfoSpaces(es->str, es->indent * 2);
01758 appendStringInfo(es->str, "Sort Method: %s %s: %ldkB\n",
01759 sortMethod, spaceType, spaceUsed);
01760 }
01761 else
01762 {
01763 ExplainPropertyText("Sort Method", sortMethod, es);
01764 ExplainPropertyLong("Sort Space Used", spaceUsed, es);
01765 ExplainPropertyText("Sort Space Type", spaceType, es);
01766 }
01767 }
01768 }
01769
01770
01771
01772
01773 static void
01774 show_hash_info(HashState *hashstate, ExplainState *es)
01775 {
01776 HashJoinTable hashtable;
01777
01778 Assert(IsA(hashstate, HashState));
01779 hashtable = hashstate->hashtable;
01780
01781 if (hashtable)
01782 {
01783 long spacePeakKb = (hashtable->spacePeak + 1023) / 1024;
01784
01785 if (es->format != EXPLAIN_FORMAT_TEXT)
01786 {
01787 ExplainPropertyLong("Hash Buckets", hashtable->nbuckets, es);
01788 ExplainPropertyLong("Hash Batches", hashtable->nbatch, es);
01789 ExplainPropertyLong("Original Hash Batches",
01790 hashtable->nbatch_original, es);
01791 ExplainPropertyLong("Peak Memory Usage", spacePeakKb, es);
01792 }
01793 else if (hashtable->nbatch_original != hashtable->nbatch)
01794 {
01795 appendStringInfoSpaces(es->str, es->indent * 2);
01796 appendStringInfo(es->str,
01797 "Buckets: %d Batches: %d (originally %d) Memory Usage: %ldkB\n",
01798 hashtable->nbuckets, hashtable->nbatch,
01799 hashtable->nbatch_original, spacePeakKb);
01800 }
01801 else
01802 {
01803 appendStringInfoSpaces(es->str, es->indent * 2);
01804 appendStringInfo(es->str,
01805 "Buckets: %d Batches: %d Memory Usage: %ldkB\n",
01806 hashtable->nbuckets, hashtable->nbatch,
01807 spacePeakKb);
01808 }
01809 }
01810 }
01811
01812
01813
01814
01815
01816
01817 static void
01818 show_instrumentation_count(const char *qlabel, int which,
01819 PlanState *planstate, ExplainState *es)
01820 {
01821 double nfiltered;
01822 double nloops;
01823
01824 if (!es->analyze || !planstate->instrument)
01825 return;
01826
01827 if (which == 2)
01828 nfiltered = planstate->instrument->nfiltered2;
01829 else
01830 nfiltered = planstate->instrument->nfiltered1;
01831 nloops = planstate->instrument->nloops;
01832
01833
01834 if (nfiltered > 0 || es->format != EXPLAIN_FORMAT_TEXT)
01835 {
01836 if (nloops > 0)
01837 ExplainPropertyFloat(qlabel, nfiltered / nloops, 0, es);
01838 else
01839 ExplainPropertyFloat(qlabel, 0.0, 0, es);
01840 }
01841 }
01842
01843
01844
01845
01846 static void
01847 show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es)
01848 {
01849 FdwRoutine *fdwroutine = fsstate->fdwroutine;
01850
01851
01852 if (fdwroutine->ExplainForeignScan != NULL)
01853 fdwroutine->ExplainForeignScan(fsstate, es);
01854 }
01855
01856
01857
01858
01859
01860
01861
01862 static const char *
01863 explain_get_index_name(Oid indexId)
01864 {
01865 const char *result;
01866
01867 if (explain_get_index_name_hook)
01868 result = (*explain_get_index_name_hook) (indexId);
01869 else
01870 result = NULL;
01871 if (result == NULL)
01872 {
01873
01874 result = get_rel_name(indexId);
01875 if (result == NULL)
01876 elog(ERROR, "cache lookup failed for index %u", indexId);
01877 result = quote_identifier(result);
01878 }
01879 return result;
01880 }
01881
01882
01883
01884
01885 static void
01886 ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
01887 ExplainState *es)
01888 {
01889 const char *indexname = explain_get_index_name(indexid);
01890
01891 if (es->format == EXPLAIN_FORMAT_TEXT)
01892 {
01893 if (ScanDirectionIsBackward(indexorderdir))
01894 appendStringInfoString(es->str, " Backward");
01895 appendStringInfo(es->str, " using %s", indexname);
01896 }
01897 else
01898 {
01899 const char *scandir;
01900
01901 switch (indexorderdir)
01902 {
01903 case BackwardScanDirection:
01904 scandir = "Backward";
01905 break;
01906 case NoMovementScanDirection:
01907 scandir = "NoMovement";
01908 break;
01909 case ForwardScanDirection:
01910 scandir = "Forward";
01911 break;
01912 default:
01913 scandir = "???";
01914 break;
01915 }
01916 ExplainPropertyText("Scan Direction", scandir, es);
01917 ExplainPropertyText("Index Name", indexname, es);
01918 }
01919 }
01920
01921
01922
01923
01924 static void
01925 ExplainScanTarget(Scan *plan, ExplainState *es)
01926 {
01927 ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
01928 }
01929
01930
01931
01932
01933 static void
01934 ExplainModifyTarget(ModifyTable *plan, ExplainState *es)
01935 {
01936 Index rti;
01937
01938
01939
01940
01941
01942 Assert(plan->resultRelations != NIL);
01943 rti = linitial_int(plan->resultRelations);
01944
01945 ExplainTargetRel((Plan *) plan, rti, es);
01946 }
01947
01948
01949
01950
01951 static void
01952 ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
01953 {
01954 char *objectname = NULL;
01955 char *namespace = NULL;
01956 const char *objecttag = NULL;
01957 RangeTblEntry *rte;
01958 char *refname;
01959
01960 rte = rt_fetch(rti, es->rtable);
01961 refname = (char *) list_nth(es->rtable_names, rti - 1);
01962 if (refname == NULL)
01963 refname = rte->eref->aliasname;
01964
01965 switch (nodeTag(plan))
01966 {
01967 case T_SeqScan:
01968 case T_IndexScan:
01969 case T_IndexOnlyScan:
01970 case T_BitmapHeapScan:
01971 case T_TidScan:
01972 case T_ForeignScan:
01973 case T_ModifyTable:
01974
01975 Assert(rte->rtekind == RTE_RELATION);
01976 objectname = get_rel_name(rte->relid);
01977 if (es->verbose)
01978 namespace = get_namespace_name(get_rel_namespace(rte->relid));
01979 objecttag = "Relation Name";
01980 break;
01981 case T_FunctionScan:
01982 {
01983 Node *funcexpr;
01984
01985
01986 Assert(rte->rtekind == RTE_FUNCTION);
01987
01988
01989
01990
01991
01992
01993
01994 funcexpr = ((FunctionScan *) plan)->funcexpr;
01995 if (funcexpr && IsA(funcexpr, FuncExpr))
01996 {
01997 Oid funcid = ((FuncExpr *) funcexpr)->funcid;
01998
01999 objectname = get_func_name(funcid);
02000 if (es->verbose)
02001 namespace =
02002 get_namespace_name(get_func_namespace(funcid));
02003 }
02004 objecttag = "Function Name";
02005 }
02006 break;
02007 case T_ValuesScan:
02008 Assert(rte->rtekind == RTE_VALUES);
02009 break;
02010 case T_CteScan:
02011
02012 Assert(rte->rtekind == RTE_CTE);
02013 Assert(!rte->self_reference);
02014 objectname = rte->ctename;
02015 objecttag = "CTE Name";
02016 break;
02017 case T_WorkTableScan:
02018
02019 Assert(rte->rtekind == RTE_CTE);
02020 Assert(rte->self_reference);
02021 objectname = rte->ctename;
02022 objecttag = "CTE Name";
02023 break;
02024 default:
02025 break;
02026 }
02027
02028 if (es->format == EXPLAIN_FORMAT_TEXT)
02029 {
02030 appendStringInfoString(es->str, " on");
02031 if (namespace != NULL)
02032 appendStringInfo(es->str, " %s.%s", quote_identifier(namespace),
02033 quote_identifier(objectname));
02034 else if (objectname != NULL)
02035 appendStringInfo(es->str, " %s", quote_identifier(objectname));
02036 if (objectname == NULL || strcmp(refname, objectname) != 0)
02037 appendStringInfo(es->str, " %s", quote_identifier(refname));
02038 }
02039 else
02040 {
02041 if (objecttag != NULL && objectname != NULL)
02042 ExplainPropertyText(objecttag, objectname, es);
02043 if (namespace != NULL)
02044 ExplainPropertyText("Schema", namespace, es);
02045 ExplainPropertyText("Alias", refname, es);
02046 }
02047 }
02048
02049
02050
02051
02052 static void
02053 show_modifytable_info(ModifyTableState *mtstate, ExplainState *es)
02054 {
02055 FdwRoutine *fdwroutine = mtstate->resultRelInfo->ri_FdwRoutine;
02056
02057
02058
02059
02060
02061
02062
02063 if (fdwroutine != NULL &&
02064 fdwroutine->ExplainForeignModify != NULL)
02065 {
02066 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
02067 List *fdw_private = (List *) linitial(node->fdwPrivLists);
02068
02069 fdwroutine->ExplainForeignModify(mtstate,
02070 mtstate->resultRelInfo,
02071 fdw_private,
02072 0,
02073 es);
02074 }
02075 }
02076
02077
02078
02079
02080
02081
02082
02083
02084
02085
02086
02087 static void
02088 ExplainMemberNodes(List *plans, PlanState **planstates,
02089 List *ancestors, ExplainState *es)
02090 {
02091 int nplans = list_length(plans);
02092 int j;
02093
02094 for (j = 0; j < nplans; j++)
02095 ExplainNode(planstates[j], ancestors,
02096 "Member", NULL, es);
02097 }
02098
02099
02100
02101
02102
02103
02104
02105 static void
02106 ExplainSubPlans(List *plans, List *ancestors,
02107 const char *relationship, ExplainState *es)
02108 {
02109 ListCell *lst;
02110
02111 foreach(lst, plans)
02112 {
02113 SubPlanState *sps = (SubPlanState *) lfirst(lst);
02114 SubPlan *sp = (SubPlan *) sps->xprstate.expr;
02115
02116 ExplainNode(sps->planstate, ancestors,
02117 relationship, sp->plan_name, es);
02118 }
02119 }
02120
02121
02122
02123
02124
02125 void
02126 ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
02127 {
02128 ListCell *lc;
02129 bool first = true;
02130
02131 switch (es->format)
02132 {
02133 case EXPLAIN_FORMAT_TEXT:
02134 appendStringInfoSpaces(es->str, es->indent * 2);
02135 appendStringInfo(es->str, "%s: ", qlabel);
02136 foreach(lc, data)
02137 {
02138 if (!first)
02139 appendStringInfoString(es->str, ", ");
02140 appendStringInfoString(es->str, (const char *) lfirst(lc));
02141 first = false;
02142 }
02143 appendStringInfoChar(es->str, '\n');
02144 break;
02145
02146 case EXPLAIN_FORMAT_XML:
02147 ExplainXMLTag(qlabel, X_OPENING, es);
02148 foreach(lc, data)
02149 {
02150 char *str;
02151
02152 appendStringInfoSpaces(es->str, es->indent * 2 + 2);
02153 appendStringInfoString(es->str, "<Item>");
02154 str = escape_xml((const char *) lfirst(lc));
02155 appendStringInfoString(es->str, str);
02156 pfree(str);
02157 appendStringInfoString(es->str, "</Item>\n");
02158 }
02159 ExplainXMLTag(qlabel, X_CLOSING, es);
02160 break;
02161
02162 case EXPLAIN_FORMAT_JSON:
02163 ExplainJSONLineEnding(es);
02164 appendStringInfoSpaces(es->str, es->indent * 2);
02165 escape_json(es->str, qlabel);
02166 appendStringInfoString(es->str, ": [");
02167 foreach(lc, data)
02168 {
02169 if (!first)
02170 appendStringInfoString(es->str, ", ");
02171 escape_json(es->str, (const char *) lfirst(lc));
02172 first = false;
02173 }
02174 appendStringInfoChar(es->str, ']');
02175 break;
02176
02177 case EXPLAIN_FORMAT_YAML:
02178 ExplainYAMLLineStarting(es);
02179 appendStringInfo(es->str, "%s: ", qlabel);
02180 foreach(lc, data)
02181 {
02182 appendStringInfoChar(es->str, '\n');
02183 appendStringInfoSpaces(es->str, es->indent * 2 + 2);
02184 appendStringInfoString(es->str, "- ");
02185 escape_yaml(es->str, (const char *) lfirst(lc));
02186 }
02187 break;
02188 }
02189 }
02190
02191
02192
02193
02194
02195
02196
02197
02198
02199
02200 static void
02201 ExplainProperty(const char *qlabel, const char *value, bool numeric,
02202 ExplainState *es)
02203 {
02204 switch (es->format)
02205 {
02206 case EXPLAIN_FORMAT_TEXT:
02207 appendStringInfoSpaces(es->str, es->indent * 2);
02208 appendStringInfo(es->str, "%s: %s\n", qlabel, value);
02209 break;
02210
02211 case EXPLAIN_FORMAT_XML:
02212 {
02213 char *str;
02214
02215 appendStringInfoSpaces(es->str, es->indent * 2);
02216 ExplainXMLTag(qlabel, X_OPENING | X_NOWHITESPACE, es);
02217 str = escape_xml(value);
02218 appendStringInfoString(es->str, str);
02219 pfree(str);
02220 ExplainXMLTag(qlabel, X_CLOSING | X_NOWHITESPACE, es);
02221 appendStringInfoChar(es->str, '\n');
02222 }
02223 break;
02224
02225 case EXPLAIN_FORMAT_JSON:
02226 ExplainJSONLineEnding(es);
02227 appendStringInfoSpaces(es->str, es->indent * 2);
02228 escape_json(es->str, qlabel);
02229 appendStringInfoString(es->str, ": ");
02230 if (numeric)
02231 appendStringInfoString(es->str, value);
02232 else
02233 escape_json(es->str, value);
02234 break;
02235
02236 case EXPLAIN_FORMAT_YAML:
02237 ExplainYAMLLineStarting(es);
02238 appendStringInfo(es->str, "%s: ", qlabel);
02239 if (numeric)
02240 appendStringInfoString(es->str, value);
02241 else
02242 escape_yaml(es->str, value);
02243 break;
02244 }
02245 }
02246
02247
02248
02249
02250 void
02251 ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
02252 {
02253 ExplainProperty(qlabel, value, false, es);
02254 }
02255
02256
02257
02258
02259 void
02260 ExplainPropertyInteger(const char *qlabel, int value, ExplainState *es)
02261 {
02262 char buf[32];
02263
02264 snprintf(buf, sizeof(buf), "%d", value);
02265 ExplainProperty(qlabel, buf, true, es);
02266 }
02267
02268
02269
02270
02271 void
02272 ExplainPropertyLong(const char *qlabel, long value, ExplainState *es)
02273 {
02274 char buf[32];
02275
02276 snprintf(buf, sizeof(buf), "%ld", value);
02277 ExplainProperty(qlabel, buf, true, es);
02278 }
02279
02280
02281
02282
02283
02284 void
02285 ExplainPropertyFloat(const char *qlabel, double value, int ndigits,
02286 ExplainState *es)
02287 {
02288 char buf[256];
02289
02290 snprintf(buf, sizeof(buf), "%.*f", ndigits, value);
02291 ExplainProperty(qlabel, buf, true, es);
02292 }
02293
02294
02295
02296
02297
02298
02299
02300
02301
02302
02303 static void
02304 ExplainOpenGroup(const char *objtype, const char *labelname,
02305 bool labeled, ExplainState *es)
02306 {
02307 switch (es->format)
02308 {
02309 case EXPLAIN_FORMAT_TEXT:
02310
02311 break;
02312
02313 case EXPLAIN_FORMAT_XML:
02314 ExplainXMLTag(objtype, X_OPENING, es);
02315 es->indent++;
02316 break;
02317
02318 case EXPLAIN_FORMAT_JSON:
02319 ExplainJSONLineEnding(es);
02320 appendStringInfoSpaces(es->str, 2 * es->indent);
02321 if (labelname)
02322 {
02323 escape_json(es->str, labelname);
02324 appendStringInfoString(es->str, ": ");
02325 }
02326 appendStringInfoChar(es->str, labeled ? '{' : '[');
02327
02328
02329
02330
02331
02332
02333
02334 es->grouping_stack = lcons_int(0, es->grouping_stack);
02335 es->indent++;
02336 break;
02337
02338 case EXPLAIN_FORMAT_YAML:
02339
02340
02341
02342
02343
02344
02345
02346 ExplainYAMLLineStarting(es);
02347 if (labelname)
02348 {
02349 appendStringInfo(es->str, "%s: ", labelname);
02350 es->grouping_stack = lcons_int(1, es->grouping_stack);
02351 }
02352 else
02353 {
02354 appendStringInfoString(es->str, "- ");
02355 es->grouping_stack = lcons_int(0, es->grouping_stack);
02356 }
02357 es->indent++;
02358 break;
02359 }
02360 }
02361
02362
02363
02364
02365
02366 static void
02367 ExplainCloseGroup(const char *objtype, const char *labelname,
02368 bool labeled, ExplainState *es)
02369 {
02370 switch (es->format)
02371 {
02372 case EXPLAIN_FORMAT_TEXT:
02373
02374 break;
02375
02376 case EXPLAIN_FORMAT_XML:
02377 es->indent--;
02378 ExplainXMLTag(objtype, X_CLOSING, es);
02379 break;
02380
02381 case EXPLAIN_FORMAT_JSON:
02382 es->indent--;
02383 appendStringInfoChar(es->str, '\n');
02384 appendStringInfoSpaces(es->str, 2 * es->indent);
02385 appendStringInfoChar(es->str, labeled ? '}' : ']');
02386 es->grouping_stack = list_delete_first(es->grouping_stack);
02387 break;
02388
02389 case EXPLAIN_FORMAT_YAML:
02390 es->indent--;
02391 es->grouping_stack = list_delete_first(es->grouping_stack);
02392 break;
02393 }
02394 }
02395
02396
02397
02398
02399
02400
02401
02402 static void
02403 ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
02404 {
02405 switch (es->format)
02406 {
02407 case EXPLAIN_FORMAT_TEXT:
02408
02409 break;
02410
02411 case EXPLAIN_FORMAT_XML:
02412 ExplainXMLTag(objtype, X_CLOSE_IMMEDIATE, es);
02413 break;
02414
02415 case EXPLAIN_FORMAT_JSON:
02416 ExplainJSONLineEnding(es);
02417 appendStringInfoSpaces(es->str, 2 * es->indent);
02418 if (labelname)
02419 {
02420 escape_json(es->str, labelname);
02421 appendStringInfoString(es->str, ": ");
02422 }
02423 escape_json(es->str, objtype);
02424 break;
02425
02426 case EXPLAIN_FORMAT_YAML:
02427 ExplainYAMLLineStarting(es);
02428 if (labelname)
02429 {
02430 escape_yaml(es->str, labelname);
02431 appendStringInfoString(es->str, ": ");
02432 }
02433 else
02434 {
02435 appendStringInfoString(es->str, "- ");
02436 }
02437 escape_yaml(es->str, objtype);
02438 break;
02439 }
02440 }
02441
02442
02443
02444
02445
02446
02447
02448 void
02449 ExplainBeginOutput(ExplainState *es)
02450 {
02451 switch (es->format)
02452 {
02453 case EXPLAIN_FORMAT_TEXT:
02454
02455 break;
02456
02457 case EXPLAIN_FORMAT_XML:
02458 appendStringInfoString(es->str,
02459 "<explain xmlns=\"http://www.postgresql.org/2009/explain\">\n");
02460 es->indent++;
02461 break;
02462
02463 case EXPLAIN_FORMAT_JSON:
02464
02465 appendStringInfoChar(es->str, '[');
02466 es->grouping_stack = lcons_int(0, es->grouping_stack);
02467 es->indent++;
02468 break;
02469
02470 case EXPLAIN_FORMAT_YAML:
02471 es->grouping_stack = lcons_int(0, es->grouping_stack);
02472 break;
02473 }
02474 }
02475
02476
02477
02478
02479 void
02480 ExplainEndOutput(ExplainState *es)
02481 {
02482 switch (es->format)
02483 {
02484 case EXPLAIN_FORMAT_TEXT:
02485
02486 break;
02487
02488 case EXPLAIN_FORMAT_XML:
02489 es->indent--;
02490 appendStringInfoString(es->str, "</explain>");
02491 break;
02492
02493 case EXPLAIN_FORMAT_JSON:
02494 es->indent--;
02495 appendStringInfoString(es->str, "\n]");
02496 es->grouping_stack = list_delete_first(es->grouping_stack);
02497 break;
02498
02499 case EXPLAIN_FORMAT_YAML:
02500 es->grouping_stack = list_delete_first(es->grouping_stack);
02501 break;
02502 }
02503 }
02504
02505
02506
02507
02508 void
02509 ExplainSeparatePlans(ExplainState *es)
02510 {
02511 switch (es->format)
02512 {
02513 case EXPLAIN_FORMAT_TEXT:
02514
02515 appendStringInfoChar(es->str, '\n');
02516 break;
02517
02518 case EXPLAIN_FORMAT_XML:
02519 case EXPLAIN_FORMAT_JSON:
02520 case EXPLAIN_FORMAT_YAML:
02521
02522 break;
02523 }
02524 }
02525
02526
02527
02528
02529
02530
02531
02532
02533
02534
02535
02536 static void
02537 ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
02538 {
02539 const char *s;
02540
02541 if ((flags & X_NOWHITESPACE) == 0)
02542 appendStringInfoSpaces(es->str, 2 * es->indent);
02543 appendStringInfoCharMacro(es->str, '<');
02544 if ((flags & X_CLOSING) != 0)
02545 appendStringInfoCharMacro(es->str, '/');
02546 for (s = tagname; *s; s++)
02547 appendStringInfoCharMacro(es->str, (*s == ' ') ? '-' : *s);
02548 if ((flags & X_CLOSE_IMMEDIATE) != 0)
02549 appendStringInfoString(es->str, " /");
02550 appendStringInfoCharMacro(es->str, '>');
02551 if ((flags & X_NOWHITESPACE) == 0)
02552 appendStringInfoCharMacro(es->str, '\n');
02553 }
02554
02555
02556
02557
02558
02559
02560
02561
02562 static void
02563 ExplainJSONLineEnding(ExplainState *es)
02564 {
02565 Assert(es->format == EXPLAIN_FORMAT_JSON);
02566 if (linitial_int(es->grouping_stack) != 0)
02567 appendStringInfoChar(es->str, ',');
02568 else
02569 linitial_int(es->grouping_stack) = 1;
02570 appendStringInfoChar(es->str, '\n');
02571 }
02572
02573
02574
02575
02576
02577
02578
02579
02580
02581
02582 static void
02583 ExplainYAMLLineStarting(ExplainState *es)
02584 {
02585 Assert(es->format == EXPLAIN_FORMAT_YAML);
02586 if (linitial_int(es->grouping_stack) == 0)
02587 {
02588 linitial_int(es->grouping_stack) = 1;
02589 }
02590 else
02591 {
02592 appendStringInfoChar(es->str, '\n');
02593 appendStringInfoSpaces(es->str, es->indent * 2);
02594 }
02595 }
02596
02597
02598
02599
02600
02601
02602
02603
02604
02605
02606
02607 static void
02608 escape_yaml(StringInfo buf, const char *str)
02609 {
02610 escape_json(buf, str);
02611 }