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  }