📄 explain.c
字号:
} appendStringInfoString(str, pname); switch (nodeTag(plan)) { case T_IndexScan: if (ScanDirectionIsBackward(((IndexScan *) plan)->indexorderdir)) appendStringInfoString(str, " Backward"); appendStringInfo(str, " using %s", quote_identifier(get_rel_name(((IndexScan *) plan)->indexid))); /* FALL THRU */ case T_SeqScan: case T_BitmapHeapScan: case T_TidScan: if (((Scan *) plan)->scanrelid > 0) { RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid, es->rtable); char *relname; /* Assume it's on a real relation */ Assert(rte->rtekind == RTE_RELATION); /* We only show the rel name, not schema name */ relname = get_rel_name(rte->relid); appendStringInfo(str, " on %s", quote_identifier(relname)); if (strcmp(rte->eref->aliasname, relname) != 0) appendStringInfo(str, " %s", quote_identifier(rte->eref->aliasname)); } break; case T_BitmapIndexScan: appendStringInfo(str, " on %s", quote_identifier(get_rel_name(((BitmapIndexScan *) plan)->indexid))); break; case T_SubqueryScan: if (((Scan *) plan)->scanrelid > 0) { RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid, es->rtable); appendStringInfo(str, " %s", quote_identifier(rte->eref->aliasname)); } break; case T_FunctionScan: if (((Scan *) plan)->scanrelid > 0) { RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid, es->rtable); char *proname; /* Assert it's on a RangeFunction */ Assert(rte->rtekind == RTE_FUNCTION); /* * If the expression is still a function call, we can get the * real name of the function. Otherwise, punt (this can * happen if the optimizer simplified away the function call, * for example). */ if (rte->funcexpr && IsA(rte->funcexpr, FuncExpr)) { FuncExpr *funcexpr = (FuncExpr *) rte->funcexpr; Oid funcid = funcexpr->funcid; /* We only show the func name, not schema name */ proname = get_func_name(funcid); } else proname = rte->eref->aliasname; appendStringInfo(str, " on %s", quote_identifier(proname)); if (strcmp(rte->eref->aliasname, proname) != 0) appendStringInfo(str, " %s", quote_identifier(rte->eref->aliasname)); } break; default: break; } appendStringInfo(str, " (cost=%.2f..%.2f rows=%.0f width=%d)", plan->startup_cost, plan->total_cost, plan->plan_rows, plan->plan_width); /* * We have to forcibly clean up the instrumentation state because we * haven't done ExecutorEnd yet. This is pretty grotty ... */ if (planstate->instrument) InstrEndLoop(planstate->instrument); if (planstate->instrument && planstate->instrument->nloops > 0) { double nloops = planstate->instrument->nloops; appendStringInfo(str, " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)", 1000.0 * planstate->instrument->startup / nloops, 1000.0 * planstate->instrument->total / nloops, planstate->instrument->ntuples / nloops, planstate->instrument->nloops); } else if (es->printAnalyze) appendStringInfo(str, " (never executed)"); appendStringInfoChar(str, '\n'); /* quals, sort keys, etc */ switch (nodeTag(plan)) { case T_IndexScan: show_scan_qual(((IndexScan *) plan)->indexqualorig, "Index Cond", ((Scan *) plan)->scanrelid, outer_plan, str, indent, es); show_scan_qual(plan->qual, "Filter", ((Scan *) plan)->scanrelid, outer_plan, str, indent, es); break; case T_BitmapIndexScan: show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig, "Index Cond", ((Scan *) plan)->scanrelid, outer_plan, str, indent, es); break; case T_BitmapHeapScan: /* XXX do we want to show this in production? */ show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig, "Recheck Cond", ((Scan *) plan)->scanrelid, outer_plan, str, indent, es); /* FALL THRU */ case T_SeqScan: case T_TidScan: case T_SubqueryScan: case T_FunctionScan: show_scan_qual(plan->qual, "Filter", ((Scan *) plan)->scanrelid, outer_plan, str, indent, es); break; case T_NestLoop: show_upper_qual(((NestLoop *) plan)->join.joinqual, "Join Filter", "outer", OUTER, outerPlan(plan), "inner", INNER, innerPlan(plan), str, indent, es); show_upper_qual(plan->qual, "Filter", "outer", OUTER, outerPlan(plan), "inner", INNER, innerPlan(plan), str, indent, es); break; case T_MergeJoin: show_upper_qual(((MergeJoin *) plan)->mergeclauses, "Merge Cond", "outer", OUTER, outerPlan(plan), "inner", INNER, innerPlan(plan), str, indent, es); show_upper_qual(((MergeJoin *) plan)->join.joinqual, "Join Filter", "outer", OUTER, outerPlan(plan), "inner", INNER, innerPlan(plan), str, indent, es); show_upper_qual(plan->qual, "Filter", "outer", OUTER, outerPlan(plan), "inner", INNER, innerPlan(plan), str, indent, es); break; case T_HashJoin: show_upper_qual(((HashJoin *) plan)->hashclauses, "Hash Cond", "outer", OUTER, outerPlan(plan), "inner", INNER, innerPlan(plan), str, indent, es); show_upper_qual(((HashJoin *) plan)->join.joinqual, "Join Filter", "outer", OUTER, outerPlan(plan), "inner", INNER, innerPlan(plan), str, indent, es); show_upper_qual(plan->qual, "Filter", "outer", OUTER, outerPlan(plan), "inner", INNER, innerPlan(plan), str, indent, es); break; case T_Agg: case T_Group: show_upper_qual(plan->qual, "Filter", "subplan", 0, outerPlan(plan), "", 0, NULL, str, indent, es); break; case T_Sort: show_sort_keys(plan->targetlist, ((Sort *) plan)->numCols, ((Sort *) plan)->sortColIdx, "Sort Key", str, indent, es); break; case T_Result: show_upper_qual((List *) ((Result *) plan)->resconstantqual, "One-Time Filter", "subplan", OUTER, outerPlan(plan), "", 0, NULL, str, indent, es); show_upper_qual(plan->qual, "Filter", "subplan", OUTER, outerPlan(plan), "", 0, NULL, str, indent, es); break; default: break; } /* initPlan-s */ if (plan->initPlan) { List *saved_rtable = es->rtable; ListCell *lst; for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " InitPlan\n"); foreach(lst, planstate->initPlan) { SubPlanState *sps = (SubPlanState *) lfirst(lst); SubPlan *sp = (SubPlan *) sps->xprstate.expr; es->rtable = sp->rtable; for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " -> "); explain_outNode(str, sp->plan, sps->planstate, NULL, indent + 4, es); } es->rtable = saved_rtable; } /* lefttree */ if (outerPlan(plan)) { for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " -> "); /* * Ordinarily we don't pass down our own outer_plan value to our child * nodes, but in bitmap scan trees we must, since the bottom * BitmapIndexScan nodes may have outer references. */ explain_outNode(str, outerPlan(plan), outerPlanState(planstate), IsA(plan, BitmapHeapScan) ? outer_plan : NULL, indent + 3, es); } /* righttree */ if (innerPlan(plan)) { for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " -> "); explain_outNode(str, innerPlan(plan), innerPlanState(planstate), outerPlan(plan), indent + 3, es); } if (IsA(plan, Append)) { Append *appendplan = (Append *) plan; AppendState *appendstate = (AppendState *) planstate; ListCell *lst; int j; j = 0; foreach(lst, appendplan->appendplans) { Plan *subnode = (Plan *) lfirst(lst); for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " -> "); explain_outNode(str, subnode, appendstate->appendplans[j], NULL, indent + 3, es); j++; } } if (IsA(plan, BitmapAnd)) { BitmapAnd *bitmapandplan = (BitmapAnd *) plan; BitmapAndState *bitmapandstate = (BitmapAndState *) planstate; ListCell *lst; int j; j = 0; foreach(lst, bitmapandplan->bitmapplans) { Plan *subnode = (Plan *) lfirst(lst); for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " -> "); explain_outNode(str, subnode, bitmapandstate->bitmapplans[j], outer_plan, /* pass down same outer plan */ indent + 3, es); j++; } } if (IsA(plan, BitmapOr)) { BitmapOr *bitmaporplan = (BitmapOr *) plan; BitmapOrState *bitmaporstate = (BitmapOrState *) planstate; ListCell *lst; int j; j = 0; foreach(lst, bitmaporplan->bitmapplans) { Plan *subnode = (Plan *) lfirst(lst); for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " -> "); explain_outNode(str, subnode, bitmaporstate->bitmapplans[j], outer_plan, /* pass down same outer plan */ indent + 3, es); j++; } } if (IsA(plan, SubqueryScan)) { SubqueryScan *subqueryscan = (SubqueryScan *) plan; SubqueryScanState *subquerystate = (SubqueryScanState *) planstate; Plan *subnode = subqueryscan->subplan; RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid, es->rtable); List *saved_rtable = es->rtable; Assert(rte->rtekind == RTE_SUBQUERY); es->rtable = rte->subquery->rtable; for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " -> "); explain_outNode(str, subnode, subquerystate->subplan, NULL, indent + 3, es); es->rtable = saved_rtable; } /* subPlan-s */ if (planstate->subPlan) { List *saved_rtable = es->rtable; ListCell *lst; for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " SubPlan\n"); foreach(lst, planstate->subPlan) { SubPlanState *sps = (SubPlanState *) lfirst(lst); SubPlan *sp = (SubPlan *) sps->xprstate.expr; es->rtable = sp->rtable; for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " -> "); explain_outNode(str, sp->plan, sps->planstate, NULL, indent + 4, es); } es->rtable = saved_rtable; }}/* * Show a qualifier expression for a scan plan node */static voidshow_scan_qual(List *qual, const char *qlabel, int scanrelid, Plan *outer_plan, StringInfo str, int indent, ExplainState *es){ RangeTblEntry *rte; Node *scancontext; Node *outercontext; List *context; Node *node; char *exprstr; int i; /* No work if empty qual */ if (qual == NIL) return; /* Convert AND list to explicit AND */ node = (Node *) make_ands_explicit(qual); /* Generate deparse context */ Assert(scanrelid > 0 && scanrelid <= list_length(es->rtable)); rte = rt_fetch(scanrelid, es->rtable); scancontext = deparse_context_for_rte(rte); /* * If we have an outer plan that is referenced by the qual, add it to the * deparse context. If not, don't (so that we don't force prefixes * unnecessarily). */ if (outer_plan) { Relids varnos = pull_varnos(node); if (bms_is_member(OUTER, varnos)) outercontext = deparse_context_for_subplan("outer", outer_plan->targetlist, es->rtable); else outercontext = NULL; bms_free(varnos); } else outercontext = NULL; context = deparse_context_for_plan(scanrelid, scancontext, OUTER, outercontext, NIL); /* Deparse the expression */ exprstr = deparse_expression(node, context, (outercontext != NULL), false); /* And add to str */ for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " %s: %s\n", qlabel, exprstr);}/* * Show a qualifier expression for an upper-level plan node */static voidshow_upper_qual(List *qual, const char *qlabel, const char *outer_name, int outer_varno, Plan *outer_plan, const char *inner_name, int inner_varno, Plan *inner_plan, StringInfo str, int indent, ExplainState *es){ List *context; Node *outercontext; Node *innercontext; Node *node; char *exprstr; int i; /* No work if empty qual */ if (qual == NIL) return; /* Generate deparse context */ if (outer_plan) outercontext = deparse_context_for_subplan(outer_name, outer_plan->targetlist, es->rtable); else outercontext = NULL; if (inner_plan) innercontext = deparse_context_for_subplan(inner_name, inner_plan->targetlist, es->rtable); else innercontext = NULL; context = deparse_context_for_plan(outer_varno, outercontext, inner_varno, innercontext, NIL); /* Deparse the expression */ node = (Node *) make_ands_explicit(qual); exprstr = deparse_expression(node, context, (inner_plan != NULL), false); /* And add to str */ for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " %s: %s\n", qlabel, exprstr);}/* * Show the sort keys for a Sort node. */static voidshow_sort_keys(List *tlist, int nkeys, AttrNumber *keycols, const char *qlabel, StringInfo str, int indent, ExplainState *es){ List *context; bool useprefix; int keyno; char *exprstr; Relids varnos; int i; if (nkeys <= 0) return; for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " %s: ", qlabel); /* * In this routine we expect that the plan node's tlist has not been * processed by set_plan_references(). Normally, any Vars will contain * valid varnos referencing the actual rtable. But we might instead be * looking at a dummy tlist generated by prepunion.c; if there are Vars * with zero varno, use the tlist itself to determine their names. */ varnos = pull_varnos((Node *) tlist); if (bms_is_member(0, varnos)) { Node *outercontext; outercontext = deparse_context_for_subplan("sort", tlist, es->rtable); context = deparse_context_for_plan(0, outercontext, 0, NULL, NIL); useprefix = false; } else { context = deparse_context_for_plan(0, NULL, 0, NULL, es->rtable); useprefix = list_length(es->rtable) > 1; } bms_free(varnos); for (keyno = 0; keyno < nkeys; keyno++) { /* find key expression in tlist */ AttrNumber keyresno = keycols[keyno]; TargetEntry *target = get_tle_by_resno(tlist, keyresno); if (!target) elog(ERROR, "no tlist entry for key %d", keyresno); /* Deparse the expression, showing any top-level cast */ exprstr = deparse_expression((Node *) target->expr, context, useprefix, true); /* And add to str */ if (keyno > 0) appendStringInfo(str, ", "); appendStringInfoString(str, exprstr); } appendStringInfo(str, "\n");}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -