// newExplainPlanNode instantiates a planNode that runs an EXPLAIN query. func (p *planner) makeExplainPlanNode(explainer explainer, expanded bool, plan planNode) planNode { columns := ResultColumns{ // Level is the depth of the node in the tree. {Name: "Level", Typ: parser.TypeInt}, // Type is the node type. {Name: "Type", Typ: parser.TypeString}, // Field is the part of the node that a row of output pertains to. // For example a select node may have separate "render" and // "filter" fields. {Name: "Field", Typ: parser.TypeString}, // Description contains details about the field. {Name: "Description", Typ: parser.TypeString}, } if explainer.showMetadata { // Columns is the type signature of the data source. columns = append(columns, ResultColumn{Name: "Columns", Typ: parser.TypeString}) // Ordering indicates the known ordering of the data from this source. columns = append(columns, ResultColumn{Name: "Ordering", Typ: parser.TypeString}) } explainer.fmtFlags = parser.FmtExpr( explainer.showTypes, explainer.symbolicVars, explainer.qualifyNames) node := &explainPlanNode{ explainer: explainer, expanded: expanded, plan: plan, results: p.newContainerValuesNode(columns, 0), } return node }
// planToString uses explain() to build a string representation of the planNode. func planToString(plan planNode) string { var buf bytes.Buffer e := explainer{ showMetadata: true, showTypes: true, showExprs: true, showSelectTop: true, fmtFlags: parser.FmtExpr(parser.FmtSimple, true, true, true), makeRow: func(level int, name, field, description string, plan planNode) { if field != "" { field = "." + field } if plan == nil { fmt.Fprintf(&buf, "%d %s%s %s\n", level, name, field, description) } else { fmt.Fprintf(&buf, "%d %s%s %s %s %s\n", level, name, field, description, formatColumns(plan.Columns(), true), plan.Ordering().AsString(plan.Columns()), ) } }, } _ = walkPlan(plan, &e) return buf.String() }