github.com/matrixorigin/matrixone@v1.2.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 "bytes" 19 "context" 20 "fmt" 21 22 "github.com/google/uuid" 23 "github.com/matrixorigin/matrixone/pkg/common/moerr" 24 "github.com/matrixorigin/matrixone/pkg/logutil" 25 "github.com/matrixorigin/matrixone/pkg/pb/plan" 26 "github.com/matrixorigin/matrixone/pkg/util/trace/impl/motrace/statistic" 27 ) 28 29 var _ ExplainQuery = &ExplainQueryImpl{} 30 31 type ExplainQueryImpl struct { 32 QueryPlan *plan.Query 33 } 34 35 func NewExplainQueryImpl(query *plan.Query) *ExplainQueryImpl { 36 return &ExplainQueryImpl{ 37 QueryPlan: query, 38 } 39 } 40 41 func (e *ExplainQueryImpl) ExplainPlan(ctx context.Context, buffer *ExplainDataBuffer, options *ExplainOptions) error { 42 nodes := e.QueryPlan.Nodes 43 44 isForest := false 45 if len(e.QueryPlan.Steps) > 1 { 46 isForest = true 47 } 48 49 for index, rootNodeID := range e.QueryPlan.Steps { 50 logutil.Debugf("------------------------------------Query Plan-%v ---------------------------------------------", index) 51 settings := FormatSettings{ 52 buffer: buffer, 53 offset: 0, 54 indent: 2, 55 level: 0, 56 } 57 58 if isForest { 59 title := fmt.Sprintf("Plan %v:", index) 60 settings.buffer.PushPlanTitle(title) 61 } 62 63 err := traversalPlan(ctx, nodes[rootNodeID], nodes, &settings, options) 64 if err != nil { 65 return err 66 } 67 } 68 return nil 69 } 70 71 func BuildJsonPlan(ctx context.Context, uuid uuid.UUID, options *ExplainOptions, query *plan.Query) *ExplainData { 72 nodes := query.Nodes 73 expdata := NewExplainData(uuid) 74 for index, rootNodeId := range query.Steps { 75 graphData := NewGraphData(len(nodes)) 76 err := PreOrderPlan(ctx, nodes[rootNodeId], nodes, graphData, options) 77 if err != nil { 78 var errdata *ExplainData 79 if moErr, ok := err.(*moerr.Error); ok { 80 errdata = NewExplainDataFail(uuid, moErr.MySQLCode(), moErr.Error()) 81 } else { 82 newError := moerr.NewInternalError(ctx, "An error occurred when plan is serialized to json") 83 errdata = NewExplainDataFail(uuid, newError.MySQLCode(), newError.Error()) 84 } 85 return errdata 86 } 87 err = graphData.StatisticsGlobalResource(ctx) 88 if err != nil { 89 var errdata *ExplainData 90 if moErr, ok := err.(*moerr.Error); ok { 91 errdata = NewExplainDataFail(uuid, moErr.MySQLCode(), moErr.Error()) 92 } else { 93 newError := moerr.NewInternalError(ctx, "An error occurred when plan is serialized to json") 94 errdata = NewExplainDataFail(uuid, newError.MySQLCode(), newError.Error()) 95 } 96 return errdata 97 } 98 99 step := NewStep(index) 100 step.GraphData = *graphData 101 102 expdata.Steps = append(expdata.Steps, *step) 103 } 104 if stats := statistic.StatsInfoFromContext(ctx); stats != nil { 105 expdata.NewPlanStats = *stats 106 } 107 return expdata 108 } 109 110 func DebugPlan(pl *plan.Plan) string { 111 if qry, ok := pl.Plan.(*plan.Plan_Query); ok { 112 impl := NewExplainQueryImpl(qry.Query) 113 buffer := NewExplainDataBuffer() 114 opt := &ExplainOptions{ 115 Verbose: false, 116 Analyze: false, 117 Format: EXPLAIN_FORMAT_TEXT, 118 } 119 err := impl.ExplainPlan(context.TODO(), buffer, opt) 120 if err != nil { 121 return fmt.Sprintf("debug plan error %s", err.Error()) 122 } 123 return buffer.ToString() 124 } else { 125 return "only support to debug query plan" 126 } 127 } 128 129 func explainStep(ctx context.Context, step *plan.Node, settings *FormatSettings, options *ExplainOptions) error { 130 nodedescImpl := NewNodeDescriptionImpl(step) 131 132 if options.Format == EXPLAIN_FORMAT_TEXT { 133 basicNodeInfo, err1 := nodedescImpl.GetNodeBasicInfo(ctx, options) 134 if err1 != nil { 135 return nil 136 } 137 settings.buffer.PushNewLine(basicNodeInfo, true, settings.level) 138 139 if nodedescImpl.Node.NodeType == plan.Node_SINK_SCAN { 140 msg := "DataSource: " 141 for i, s := range nodedescImpl.Node.SourceStep { 142 if i > 0 { 143 msg += ", " 144 } 145 msg += fmt.Sprintf("Plan %v", s) 146 } 147 settings.buffer.PushNewLine(msg, false, settings.level) 148 } 149 150 if nodedescImpl.Node.NodeType == plan.Node_RECURSIVE_SCAN { 151 msg := "DataSource: " 152 for i, s := range nodedescImpl.Node.SourceStep { 153 if i > 0 { 154 msg += ", " 155 } 156 msg += fmt.Sprintf("Plan %v", s) 157 } 158 settings.buffer.PushNewLine(msg, false, settings.level) 159 } 160 161 if nodedescImpl.Node.NodeType == plan.Node_RECURSIVE_CTE { 162 msg := "DataSource: " 163 for i, s := range nodedescImpl.Node.SourceStep { 164 if i > 0 { 165 msg += ", " 166 } 167 msg += fmt.Sprintf("Plan %v", s) 168 } 169 settings.buffer.PushNewLine(msg, false, settings.level) 170 } 171 172 // Process verbose optioan information , "Output:" 173 if options.Verbose { 174 if nodedescImpl.Node.GetProjectList() != nil { 175 projecrtInfo, err := nodedescImpl.GetProjectListInfo(ctx, options) 176 if err != nil { 177 return err 178 } 179 settings.buffer.PushNewLine(projecrtInfo, false, settings.level) 180 } 181 182 if nodedescImpl.Node.NodeType == plan.Node_TABLE_SCAN { 183 if nodedescImpl.Node.TableDef != nil { 184 tableDef, err := nodedescImpl.GetTableDef(ctx, options) 185 if err != nil { 186 return err 187 } 188 settings.buffer.PushNewLine(tableDef, false, settings.level) 189 } 190 } 191 192 if nodedescImpl.Node.NodeType == plan.Node_VALUE_SCAN { 193 if nodedescImpl.Node.RowsetData != nil { 194 rowsetDataDescImpl := &RowsetDataDescribeImpl{ 195 RowsetData: nodedescImpl.Node.RowsetData, 196 } 197 // Provide a relatively balanced initial capacity [360] for byte slice to prevent multiple memory requests 198 buf := bytes.NewBuffer(make([]byte, 0, 360)) 199 err := rowsetDataDescImpl.GetDescription(ctx, options, buf) 200 if err != nil { 201 return err 202 } 203 settings.buffer.PushNewLine(buf.String(), false, settings.level) 204 } 205 } 206 207 if nodedescImpl.Node.NodeType == plan.Node_LOCK_OP { 208 if nodedescImpl.Node.LockTargets != nil { 209 buf := bytes.NewBuffer(make([]byte, 0, 360)) 210 buf.WriteString("Lock level: ") 211 if nodedescImpl.Node.LockTargets[0].LockTable { 212 buf.WriteString("Table level lock") 213 } else { 214 buf.WriteString("Row level lock") 215 } 216 settings.buffer.PushNewLine(buf.String(), false, settings.level) 217 } 218 } 219 } 220 221 // print out the actual operation information 222 if options.Analyze { 223 if nodedescImpl.Node.AnalyzeInfo != nil { 224 analyze, err := nodedescImpl.GetActualAnalyzeInfo(ctx, options) 225 if err != nil { 226 return err 227 } 228 settings.buffer.PushNewLine(analyze, false, settings.level) 229 } 230 } 231 232 // Get other node descriptions, such as "Filter:", "Group Key:", "Sort Key:" 233 extraInfo, err := nodedescImpl.GetExtraInfo(ctx, options) 234 if err != nil { 235 return err 236 } 237 for _, line := range extraInfo { 238 settings.buffer.PushNewLine(line, false, settings.level) 239 } 240 } else if options.Format == EXPLAIN_FORMAT_JSON { 241 return moerr.NewNYI(ctx, "explain format json") 242 } else if options.Format == EXPLAIN_FORMAT_DOT { 243 return moerr.NewNYI(ctx, "explain format dot") 244 } 245 return nil 246 } 247 248 func traversalPlan(ctx context.Context, node *plan.Node, Nodes []*plan.Node, settings *FormatSettings, options *ExplainOptions) error { 249 if node == nil { 250 return nil 251 } 252 err1 := explainStep(ctx, node, settings, options) 253 if err1 != nil { 254 return err1 255 } 256 settings.level++ 257 // Recursive traversal Query Plan 258 if len(node.Children) > 0 { 259 for _, childNodeID := range node.Children { 260 index, err := serachNodeIndex(ctx, childNodeID, Nodes) 261 if err != nil { 262 return err 263 } 264 err = traversalPlan(ctx, Nodes[index], Nodes, settings, options) 265 if err != nil { 266 return err 267 } 268 } 269 } 270 settings.level-- 271 return nil 272 } 273 274 // serach target node's index in Nodes slice 275 func serachNodeIndex(ctx context.Context, nodeID int32, Nodes []*plan.Node) (int32, error) { 276 for i, node := range Nodes { 277 if node.NodeId == nodeID { 278 return int32(i), nil 279 } 280 } 281 return -1, moerr.NewInvalidInput(ctx, "invliad plan nodeID %d", nodeID) 282 } 283 284 func PreOrderPlan(ctx context.Context, node *plan.Node, Nodes []*plan.Node, graphData *GraphData, options *ExplainOptions) error { 285 if node != nil { 286 newNode, err := ConvertNode(ctx, node, options) 287 if err != nil { 288 return err 289 } 290 graphData.Nodes = append(graphData.Nodes, *newNode) 291 if len(node.Children) > 0 { 292 for _, childNodeID := range node.Children { 293 index, err2 := serachNodeIndex(ctx, childNodeID, Nodes) 294 if err2 != nil { 295 return err2 296 } 297 298 edge := buildEdge(node, Nodes[index], index) 299 graphData.Edges = append(graphData.Edges, *edge) 300 301 err = PreOrderPlan(ctx, Nodes[index], Nodes, graphData, options) 302 if err != nil { 303 return err 304 } 305 } 306 } 307 } 308 return nil 309 } 310 311 // StatisticsRead statistics read rows, size in ExplainData 312 // 313 // Deprecated: please use explain.GetInputRowsAndInputSize instead. 314 func (d *ExplainData) StatisticsRead() (rows int64, size int64) { 315 for _, step := range d.Steps { 316 for _, node := range step.GraphData.Nodes { 317 if node.Name != TableScan && node.Name != ExternalScan { 318 continue 319 } 320 for _, s := range node.Statistics.Throughput { 321 switch s.Name { 322 case InputRows: 323 rows += s.Value 324 case InputSize: 325 size += s.Value 326 } 327 } 328 } 329 } 330 return 331 } 332 333 // Statistics of global resource usage, adding resources of all nodes 334 func (graphData *GraphData) StatisticsGlobalResource(ctx context.Context) error { 335 if graphData == nil { 336 return moerr.NewInternalError(ctx, "explain graphData data is null") 337 } else { 338 // time 339 gtimeConsumed := NewStatisticValue(TimeConsumed, "ns") 340 gwaitTime := NewStatisticValue(WaitTime, "ns") 341 342 // Throughput 343 ginputRows := NewStatisticValue(InputRows, "count") 344 goutputRows := NewStatisticValue(OutputRows, "count") 345 ginputSize := NewStatisticValue(InputSize, "byte") 346 goutputSize := NewStatisticValue(OutputSize, "byte") 347 348 // memory 349 gMemorySize := NewStatisticValue(MemorySize, "byte") 350 351 //io 352 gDiskIO := NewStatisticValue(DiskIO, "byte") 353 gS3IOByte := NewStatisticValue(S3IOByte, "byte") 354 gS3IOInputCount := NewStatisticValue(S3IOInputCount, "count") 355 gS3IOOutputCount := NewStatisticValue(S3IOOutputCount, "count") 356 357 // network 358 gNetwork := NewStatisticValue(Network, "byte") 359 360 gtotalStats := TotalStats{ 361 Name: "Time spent", 362 Value: 0, 363 Unit: "ns", 364 } 365 366 for _, node := range graphData.Nodes { 367 for _, timeStatValue := range node.Statistics.Time { 368 if timeStatValue.Name == TimeConsumed { 369 gtimeConsumed.Value += timeStatValue.Value 370 } 371 if timeStatValue.Name == WaitTime { 372 gwaitTime.Value += timeStatValue.Value 373 } 374 } 375 376 for _, throughputValue := range node.Statistics.Throughput { 377 if throughputValue.Name == InputRows { 378 ginputRows.Value += throughputValue.Value 379 } 380 if throughputValue.Name == OutputRows { 381 goutputRows.Value += throughputValue.Value 382 } 383 if throughputValue.Name == InputSize { 384 ginputSize.Value += throughputValue.Value 385 } 386 if throughputValue.Name == OutputSize { 387 goutputSize.Value += throughputValue.Value 388 } 389 } 390 391 for _, memoryValue := range node.Statistics.Memory { 392 if memoryValue.Name == MemorySize { 393 gMemorySize.Value += memoryValue.Value 394 } 395 } 396 397 for _, ioValue := range node.Statistics.IO { 398 if ioValue.Name == DiskIO { 399 gDiskIO.Value += ioValue.Value 400 } 401 if ioValue.Name == S3IOByte { 402 gS3IOByte.Value += ioValue.Value 403 } 404 if ioValue.Name == S3IOInputCount { 405 gS3IOInputCount.Value += ioValue.Value 406 } 407 if ioValue.Name == S3IOOutputCount { 408 gS3IOOutputCount.Value += ioValue.Value 409 } 410 } 411 412 for _, networkValue := range node.Statistics.Network { 413 if networkValue.Name == Network { 414 gNetwork.Value += networkValue.Value 415 } 416 } 417 gtotalStats.Value += node.TotalStats.Value 418 } 419 420 times := []StatisticValue{*gtimeConsumed, *gwaitTime} 421 mbps := []StatisticValue{*ginputRows, *goutputRows, *ginputSize, *goutputSize} 422 mems := []StatisticValue{*gMemorySize} 423 io := []StatisticValue{*gDiskIO, *gS3IOByte, *gS3IOInputCount, *gS3IOOutputCount} 424 nw := []StatisticValue{*gNetwork} 425 426 graphData.Global.Statistics.Time = append(graphData.Global.Statistics.Time, times...) 427 graphData.Global.Statistics.Throughput = append(graphData.Global.Statistics.Throughput, mbps...) 428 graphData.Global.Statistics.Memory = append(graphData.Global.Statistics.Memory, mems...) 429 graphData.Global.Statistics.IO = append(graphData.Global.Statistics.IO, io...) 430 graphData.Global.Statistics.Network = append(graphData.Global.Statistics.Network, nw...) 431 432 graphData.Global.TotalStats = gtotalStats 433 } 434 return nil 435 }