| 134 | } |
| 135 | |
| 136 | func (n *Node) RenderTreeWithStats(planNodes []*pb.PlanNode) ([]QueryPlanRow, error) { |
| 137 | tree := treeprint.New() |
| 138 | renderTreeWithStats(tree, "", n) |
| 139 | var result []QueryPlanRow |
| 140 | for _, line := range strings.Split(tree.String(), "\n") { |
| 141 | if line == "" { |
| 142 | continue |
| 143 | } |
| 144 | |
| 145 | split := strings.SplitN(line, "\t", 2) |
| 146 | // Handle the case of the root node of treeprint |
| 147 | if len(split) != 2 { |
| 148 | return nil, fmt.Errorf("unexpected split error, tree line = %q", line) |
| 149 | } |
| 150 | branchText, protojsonText := split[0], split[1] |
| 151 | |
| 152 | var planNode queryPlanNodeWithStatsTyped |
| 153 | if err := json.Unmarshal([]byte(protojsonText), &planNode); err != nil { |
| 154 | return nil, fmt.Errorf("unexpected JSON unmarshal error, tree line = %q", line) |
| 155 | } |
| 156 | |
| 157 | var text string |
| 158 | if planNode.LinkType != "" { |
| 159 | text = fmt.Sprintf("[%s] %s", planNode.LinkType, planNode.DisplayName) |
| 160 | } else { |
| 161 | text = planNode.DisplayName |
| 162 | } |
| 163 | |
| 164 | var predicates []string |
| 165 | for _, cl := range planNodes[planNode.ID].GetChildLinks() { |
| 166 | if !isPredicate(planNodes, cl) { |
| 167 | continue |
| 168 | } |
| 169 | predicates = append(predicates, fmt.Sprintf("%s: %s", cl.GetType(), planNodes[cl.ChildIndex].GetShortRepresentation().GetDescription())) |
| 170 | } |
| 171 | |
| 172 | result = append(result, QueryPlanRow{ |
| 173 | ID: planNode.ID, |
| 174 | Predicates: predicates, |
| 175 | Text: branchText + text, |
| 176 | RowsTotal: planNode.ExecutionStats.Rows.Total, |
| 177 | Execution: planNode.ExecutionStats.ExecutionSummary.NumExecutions, |
| 178 | LatencyTotal: planNode.ExecutionStats.Latency.String(), |
| 179 | }) |
| 180 | } |
| 181 | return result, nil |
| 182 | } |
| 183 | |
| 184 | func (n *Node) IsRoot() bool { |
| 185 | return n.PlanNode.Index == 0 |