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  }