github.com/matrixorigin/matrixone@v0.7.0/pkg/sql/plan/explain/explain_query.go (about) 1 // Copyright 2021 - 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package explain 16 17 import ( 18 "context" 19 20 "github.com/google/uuid" 21 "github.com/matrixorigin/matrixone/pkg/common/moerr" 22 "github.com/matrixorigin/matrixone/pkg/logutil" 23 "github.com/matrixorigin/matrixone/pkg/pb/plan" 24 ) 25 26 var _ ExplainQuery = &ExplainQueryImpl{} 27 28 type ExplainQueryImpl struct { 29 QueryPlan *plan.Query 30 } 31 32 func NewExplainQueryImpl(query *plan.Query) *ExplainQueryImpl { 33 return &ExplainQueryImpl{ 34 QueryPlan: query, 35 } 36 } 37 38 func (e *ExplainQueryImpl) ExplainPlan(ctx context.Context, buffer *ExplainDataBuffer, options *ExplainOptions) error { 39 nodes := e.QueryPlan.Nodes 40 for index, rootNodeID := range e.QueryPlan.Steps { 41 logutil.Infof("------------------------------------Query Plan-%v ---------------------------------------------", index) 42 settings := FormatSettings{ 43 buffer: buffer, 44 offset: 0, 45 indent: 2, 46 level: 0, 47 } 48 err := traversalPlan(ctx, nodes[rootNodeID], nodes, &settings, options) 49 if err != nil { 50 return err 51 } 52 } 53 return nil 54 } 55 56 func (e *ExplainQueryImpl) BuildJsonPlan(ctx context.Context, uuid uuid.UUID, options *ExplainOptions) *ExplainData { 57 nodes := e.QueryPlan.Nodes 58 expdata := NewExplainData(uuid) 59 for index, rootNodeId := range e.QueryPlan.Steps { 60 graphData := NewGraphData() 61 err := PreOrderPlan(ctx, nodes[rootNodeId], nodes, graphData, options) 62 if err != nil { 63 var errdata *ExplainData 64 if moErr, ok := err.(*moerr.Error); ok { 65 errdata = NewExplainDataFail(uuid, moErr.MySQLCode(), moErr.Error()) 66 } else { 67 newError := moerr.NewInternalError(ctx, "An error occurred when plan is serialized to json") 68 errdata = NewExplainDataFail(uuid, newError.MySQLCode(), newError.Error()) 69 } 70 return errdata 71 } 72 err = graphData.StatisticsGlobalResource(ctx) 73 if err != nil { 74 var errdata *ExplainData 75 if moErr, ok := err.(*moerr.Error); ok { 76 errdata = NewExplainDataFail(uuid, moErr.MySQLCode(), moErr.Error()) 77 } else { 78 newError := moerr.NewInternalError(ctx, "An error occurred when plan is serialized to json") 79 errdata = NewExplainDataFail(uuid, newError.MySQLCode(), newError.Error()) 80 } 81 return errdata 82 } 83 84 step := NewStep(index) 85 step.GraphData = *graphData 86 87 expdata.Steps = append(expdata.Steps, *step) 88 } 89 return expdata 90 } 91 92 func explainStep(ctx context.Context, step *plan.Node, settings *FormatSettings, options *ExplainOptions) error { 93 nodedescImpl := NewNodeDescriptionImpl(step) 94 95 if options.Format == EXPLAIN_FORMAT_TEXT { 96 basicNodeInfo, err1 := nodedescImpl.GetNodeBasicInfo(ctx, options) 97 if err1 != nil { 98 return nil 99 } 100 settings.buffer.PushNewLine(basicNodeInfo, true, settings.level) 101 102 // Process verbose optioan information , "Output:" 103 if options.Verbose { 104 if nodedescImpl.Node.GetProjectList() != nil { 105 projecrtInfo, err := nodedescImpl.GetProjectListInfo(ctx, options) 106 if err != nil { 107 return err 108 } 109 settings.buffer.PushNewLine(projecrtInfo, false, settings.level) 110 } 111 112 if nodedescImpl.Node.NodeType == plan.Node_TABLE_SCAN { 113 if nodedescImpl.Node.TableDef != nil { 114 tableDef, err := nodedescImpl.GetTableDef(ctx, options) 115 if err != nil { 116 return err 117 } 118 settings.buffer.PushNewLine(tableDef, false, settings.level) 119 } 120 } 121 122 if nodedescImpl.Node.NodeType == plan.Node_VALUE_SCAN { 123 if nodedescImpl.Node.RowsetData != nil { 124 rowsetDataDescImpl := &RowsetDataDescribeImpl{ 125 RowsetData: nodedescImpl.Node.RowsetData, 126 } 127 rowsetInfo, err := rowsetDataDescImpl.GetDescription(ctx, options) 128 if err != nil { 129 return err 130 } 131 settings.buffer.PushNewLine(rowsetInfo, false, settings.level) 132 } 133 } 134 135 if nodedescImpl.Node.NodeType == plan.Node_UPDATE { 136 if nodedescImpl.Node.UpdateCtx != nil { 137 updateCtxsDescImpl := &UpdateCtxsDescribeImpl{ 138 UpdateCtx: nodedescImpl.Node.UpdateCtx, 139 } 140 updateCols, err := updateCtxsDescImpl.GetDescription(ctx, options) 141 if err != nil { 142 return err 143 } 144 settings.buffer.PushNewLine(updateCols, false, settings.level) 145 } 146 } 147 } 148 149 // print out the actual operation information 150 if options.Analyze { 151 if nodedescImpl.Node.AnalyzeInfo != nil { 152 analyze, err := nodedescImpl.GetActualAnalyzeInfo(ctx, options) 153 if err != nil { 154 return err 155 } 156 settings.buffer.PushNewLine(analyze, false, settings.level) 157 } 158 } 159 160 // Get other node descriptions, such as "Filter:", "Group Key:", "Sort Key:" 161 extraInfo, err := nodedescImpl.GetExtraInfo(ctx, options) 162 if err != nil { 163 return err 164 } 165 for _, line := range extraInfo { 166 settings.buffer.PushNewLine(line, false, settings.level) 167 } 168 } else if options.Format == EXPLAIN_FORMAT_JSON { 169 return moerr.NewNYI(ctx, "explain format json") 170 } else if options.Format == EXPLAIN_FORMAT_DOT { 171 return moerr.NewNYI(ctx, "explain format dot") 172 } 173 return nil 174 } 175 176 func traversalPlan(ctx context.Context, node *plan.Node, Nodes []*plan.Node, settings *FormatSettings, options *ExplainOptions) error { 177 if node == nil { 178 return nil 179 } 180 err1 := explainStep(ctx, node, settings, options) 181 if err1 != nil { 182 return err1 183 } 184 settings.level++ 185 // Recursive traversal Query Plan 186 if len(node.Children) > 0 { 187 for _, childNodeID := range node.Children { 188 index, err := serachNodeIndex(ctx, childNodeID, Nodes) 189 if err != nil { 190 return err 191 } 192 err = traversalPlan(ctx, Nodes[index], Nodes, settings, options) 193 if err != nil { 194 return err 195 } 196 } 197 } 198 settings.level-- 199 return nil 200 } 201 202 // serach target node's index in Nodes slice 203 func serachNodeIndex(ctx context.Context, nodeID int32, Nodes []*plan.Node) (int32, error) { 204 for i, node := range Nodes { 205 if node.NodeId == nodeID { 206 return int32(i), nil 207 } 208 } 209 return -1, moerr.NewInvalidInput(ctx, "invliad plan nodeID %d", nodeID) 210 } 211 212 func PreOrderPlan(ctx context.Context, node *plan.Node, Nodes []*plan.Node, graphData *GraphData, options *ExplainOptions) error { 213 if node != nil { 214 newNode, err := ConvertNode(ctx, node, options) 215 if err != nil { 216 return err 217 } 218 graphData.Nodes = append(graphData.Nodes, *newNode) 219 if len(node.Children) > 0 { 220 for _, childNodeID := range node.Children { 221 index, err2 := serachNodeIndex(ctx, childNodeID, Nodes) 222 if err2 != nil { 223 return err2 224 } 225 226 edge := buildEdge(node, Nodes[index], index) 227 graphData.Edges = append(graphData.Edges, *edge) 228 229 err = PreOrderPlan(ctx, Nodes[index], Nodes, graphData, options) 230 if err != nil { 231 return err 232 } 233 } 234 } 235 } 236 return nil 237 } 238 239 // StatisticsRead statistics read rows, size in ExplainData 240 func (d *ExplainData) StatisticsRead() (rows int64, size int64) { 241 for _, step := range d.Steps { 242 for _, node := range step.GraphData.Nodes { 243 if node.Name != TableScan && node.Name != ExternalScan { 244 continue 245 } 246 for _, s := range node.Statistics.Throughput { 247 switch s.Name { 248 case InputRows: 249 rows += s.Value 250 case InputSize: 251 size += s.Value 252 } 253 } 254 } 255 } 256 return 257 } 258 259 // Statistics of global resource usage, adding resources of all nodes 260 func (graphData *GraphData) StatisticsGlobalResource(ctx context.Context) error { 261 if graphData == nil { 262 return moerr.NewInternalError(ctx, "explain graphData data is null") 263 } else { 264 // time 265 gtimeConsumed := NewStatisticValue(TimeConsumed, "us") 266 gwaitTime := NewStatisticValue(WaitTime, "us") 267 268 // Throughput 269 ginputRows := NewStatisticValue(InputRows, "count") 270 goutputRows := NewStatisticValue(OutputRows, "count") 271 ginputSize := NewStatisticValue(InputSize, "byte") 272 goutputSize := NewStatisticValue(OutputSize, "byte") 273 274 // memory 275 gMemorySize := NewStatisticValue(MemorySize, "byte") 276 277 //io 278 gDiskIO := NewStatisticValue(DiskIO, "byte") 279 gS3IOByte := NewStatisticValue(S3IOByte, "byte") 280 gS3IOCount := NewStatisticValue(S3IOCount, "count") 281 282 // network 283 gNetwork := NewStatisticValue(Network, "byte") 284 285 gtotalStats := TotalStats{ 286 Name: "Time spent", 287 Value: 0, 288 Unit: "us", 289 } 290 291 for _, node := range graphData.Nodes { 292 for _, timeStatValue := range node.Statistics.Time { 293 if timeStatValue.Name == TimeConsumed { 294 gtimeConsumed.Value += timeStatValue.Value 295 } 296 if timeStatValue.Name == WaitTime { 297 gwaitTime.Value += timeStatValue.Value 298 } 299 } 300 301 for _, throughputValue := range node.Statistics.Throughput { 302 if throughputValue.Name == InputRows { 303 ginputRows.Value += throughputValue.Value 304 } 305 if throughputValue.Name == OutputRows { 306 goutputRows.Value += throughputValue.Value 307 } 308 if throughputValue.Name == InputSize { 309 ginputSize.Value += throughputValue.Value 310 } 311 if throughputValue.Name == OutputSize { 312 goutputSize.Value += throughputValue.Value 313 } 314 } 315 316 for _, memoryValue := range node.Statistics.Memory { 317 if memoryValue.Name == MemorySize { 318 gMemorySize.Value += memoryValue.Value 319 } 320 } 321 322 for _, ioValue := range node.Statistics.IO { 323 if ioValue.Name == DiskIO { 324 gDiskIO.Value += ioValue.Value 325 } 326 if ioValue.Name == S3IOByte { 327 gS3IOByte.Value += ioValue.Value 328 } 329 if ioValue.Name == S3IOCount { 330 gS3IOCount.Value += ioValue.Value 331 } 332 } 333 334 for _, networkValue := range node.Statistics.Network { 335 if networkValue.Name == Network { 336 gNetwork.Value += networkValue.Value 337 } 338 } 339 gtotalStats.Value += node.TotalStats.Value 340 } 341 342 times := []StatisticValue{*gtimeConsumed, *gwaitTime} 343 mbps := []StatisticValue{*ginputRows, *goutputRows, *ginputSize, *goutputSize} 344 mems := []StatisticValue{*gMemorySize} 345 io := []StatisticValue{*gDiskIO, *gS3IOByte, *gS3IOCount} 346 nw := []StatisticValue{*gNetwork} 347 348 graphData.Global.Statistics.Time = append(graphData.Global.Statistics.Time, times...) 349 graphData.Global.Statistics.Throughput = append(graphData.Global.Statistics.Throughput, mbps...) 350 graphData.Global.Statistics.Memory = append(graphData.Global.Statistics.Memory, mems...) 351 graphData.Global.Statistics.IO = append(graphData.Global.Statistics.IO, io...) 352 graphData.Global.Statistics.Network = append(graphData.Global.Statistics.Network, nw...) 353 354 graphData.Global.TotalStats = gtotalStats 355 } 356 return nil 357 }