github.com/matrixorigin/matrixone@v0.7.0/pkg/sql/plan/explain/explain_node.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  	"strconv"
    20  
    21  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    22  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    23  )
    24  
    25  var _ NodeDescribe = &NodeDescribeImpl{}
    26  
    27  type NodeDescribeImpl struct {
    28  	Node *plan.Node
    29  }
    30  
    31  func NewNodeDescriptionImpl(node *plan.Node) *NodeDescribeImpl {
    32  	return &NodeDescribeImpl{
    33  		Node: node,
    34  	}
    35  }
    36  
    37  const TableScan = "Table Scan"
    38  const ExternalScan = "External Scan"
    39  
    40  func (ndesc *NodeDescribeImpl) GetNodeBasicInfo(ctx context.Context, options *ExplainOptions) (string, error) {
    41  	var result string
    42  	var pname string /* node type name for text output */
    43  
    44  	// Get the Node Name
    45  	switch ndesc.Node.NodeType {
    46  	case plan.Node_UNKNOWN:
    47  		pname = "UnKnow Node"
    48  	case plan.Node_VALUE_SCAN:
    49  		pname = "Values Scan"
    50  	case plan.Node_TABLE_SCAN:
    51  		pname = TableScan
    52  	case plan.Node_EXTERNAL_SCAN:
    53  		pname = ExternalScan
    54  	case plan.Node_MATERIAL_SCAN:
    55  		pname = "Material Scan"
    56  	case plan.Node_PROJECT:
    57  		pname = "Project"
    58  	case plan.Node_EXTERNAL_FUNCTION:
    59  		pname = "External Function"
    60  	case plan.Node_MATERIAL:
    61  		pname = "Material"
    62  	case plan.Node_RECURSIVE_CTE:
    63  		pname = "Recursive CTE"
    64  	case plan.Node_SINK:
    65  		pname = "Sink"
    66  	case plan.Node_SINK_SCAN:
    67  		pname = "Sink Scan"
    68  	case plan.Node_AGG:
    69  		pname = "Aggregate"
    70  	case plan.Node_DISTINCT:
    71  		pname = "Distinct"
    72  	case plan.Node_FILTER:
    73  		pname = "Filter"
    74  	case plan.Node_JOIN:
    75  		pname = "Join"
    76  	case plan.Node_SAMPLE:
    77  		pname = "Sample"
    78  	case plan.Node_SORT:
    79  		pname = "Sort"
    80  	case plan.Node_UNION:
    81  		pname = "Union"
    82  	case plan.Node_UNION_ALL:
    83  		pname = "Union All"
    84  	case plan.Node_UNIQUE:
    85  		pname = "Unique"
    86  	case plan.Node_WINDOW:
    87  		pname = "Window"
    88  	case plan.Node_BROADCAST:
    89  		pname = "Broadcast"
    90  	case plan.Node_SPLIT:
    91  		pname = "Split"
    92  	case plan.Node_GATHER:
    93  		pname = "Gather"
    94  	case plan.Node_ASSERT:
    95  		pname = "Assert"
    96  	case plan.Node_INSERT:
    97  		pname = "Insert"
    98  	case plan.Node_UPDATE:
    99  		pname = "Update"
   100  	case plan.Node_DELETE:
   101  		pname = "Delete"
   102  	case plan.Node_INTERSECT:
   103  		pname = "Intersect"
   104  	case plan.Node_INTERSECT_ALL:
   105  		pname = "Intersect All"
   106  	case plan.Node_MINUS:
   107  		pname = "Minus"
   108  	case plan.Node_MINUS_ALL:
   109  		pname = "Minus All"
   110  	case plan.Node_FUNCTION_SCAN:
   111  		pname = ndesc.Node.TableDef.TblFunc.Name
   112  	default:
   113  		panic("error node type")
   114  	}
   115  
   116  	// Get Node's operator object info ,such as table, view
   117  	if options.Format == EXPLAIN_FORMAT_TEXT {
   118  		result += pname
   119  		switch ndesc.Node.NodeType {
   120  		case plan.Node_VALUE_SCAN:
   121  			result += " \"*VALUES*\" "
   122  		case plan.Node_TABLE_SCAN, plan.Node_FUNCTION_SCAN, plan.Node_EXTERNAL_SCAN, plan.Node_MATERIAL_SCAN, plan.Node_INSERT:
   123  			result += " on "
   124  			if ndesc.Node.ObjRef != nil {
   125  				result += ndesc.Node.ObjRef.GetSchemaName() + "." + ndesc.Node.ObjRef.GetObjName()
   126  			} else if ndesc.Node.TableDef != nil {
   127  				result += ndesc.Node.TableDef.GetName()
   128  			}
   129  		case plan.Node_UPDATE:
   130  			result += " on "
   131  			if ndesc.Node.UpdateCtx != nil {
   132  				first := true
   133  				for _, ctx := range ndesc.Node.UpdateCtx.Ref {
   134  					if !first {
   135  						result += ", "
   136  					}
   137  					result += ctx.SchemaName + "." + ctx.ObjName
   138  					if first {
   139  						first = false
   140  					}
   141  				}
   142  			}
   143  		case plan.Node_DELETE:
   144  			result += " on "
   145  			if ndesc.Node.DeleteCtx != nil {
   146  				first := true
   147  				for _, ctx := range ndesc.Node.DeleteCtx.Ref {
   148  					if !first {
   149  						result += ", "
   150  					}
   151  					result += ctx.SchemaName + "." + ctx.ObjName
   152  					if first {
   153  						first = false
   154  					}
   155  				}
   156  			}
   157  		}
   158  	}
   159  
   160  	// Get Costs info of Node
   161  	if options.Format == EXPLAIN_FORMAT_TEXT {
   162  		//result += " (cost=%.2f..%.2f rows=%.0f width=%f)"
   163  
   164  		if options.Verbose {
   165  			costDescImpl := &CostDescribeImpl{
   166  				Stats: ndesc.Node.GetStats(),
   167  			}
   168  			costInfo, err := costDescImpl.GetDescription(ctx, options)
   169  			if err != nil {
   170  				return result, err
   171  			}
   172  			result += " " + costInfo
   173  		}
   174  	} else if options.Format == EXPLAIN_FORMAT_JSON {
   175  		return result, moerr.NewNYI(ctx, "explain format json")
   176  	} else if options.Format == EXPLAIN_FORMAT_DOT {
   177  		return result, moerr.NewNYI(ctx, "explain format dot")
   178  	}
   179  	return result, nil
   180  }
   181  
   182  func (ndesc *NodeDescribeImpl) GetActualAnalyzeInfo(ctx context.Context, options *ExplainOptions) (string, error) {
   183  	result := "Analyze: "
   184  	if ndesc.Node.AnalyzeInfo != nil {
   185  		impl := NewAnalyzeInfoDescribeImpl(ndesc.Node.AnalyzeInfo)
   186  		describe, err := impl.GetDescription(ctx, options)
   187  		if err != nil {
   188  			return result, err
   189  		}
   190  		result += describe
   191  	} else {
   192  		result += "timeConsumed=0ns waitTime=0ns inputRows=0  outputRows=0 inputSize=0 bytes outputSize:0 bytes, memorySize=0 bytes"
   193  	}
   194  	return result, nil
   195  }
   196  
   197  func (ndesc *NodeDescribeImpl) GetTableDef(ctx context.Context, options *ExplainOptions) (string, error) {
   198  	result := "Table: "
   199  	if ndesc.Node.NodeType == plan.Node_TABLE_SCAN {
   200  		tableDef := ndesc.Node.TableDef
   201  		result += "'" + tableDef.Name + "' ("
   202  		first := true
   203  		for i, col := range tableDef.Cols {
   204  			if !first {
   205  				result += ", "
   206  			}
   207  			first = false
   208  			//result += "'" + col.Name + "':" + col.Typ.Id.String()
   209  			result += strconv.Itoa(i) + ":'" + col.Name + "'"
   210  		}
   211  		result += ")"
   212  	} else {
   213  		panic("implement me")
   214  	}
   215  	return result, nil
   216  }
   217  
   218  func (ndesc *NodeDescribeImpl) GetExtraInfo(ctx context.Context, options *ExplainOptions) ([]string, error) {
   219  	lines := make([]string, 0)
   220  	// Get Sort list info
   221  	if len(ndesc.Node.OrderBy) > 0 {
   222  		orderByInfo, err := ndesc.GetOrderByInfo(ctx, options)
   223  		if err != nil {
   224  			return nil, err
   225  		}
   226  		lines = append(lines, orderByInfo)
   227  	}
   228  
   229  	// Get Join type info
   230  	if ndesc.Node.NodeType == plan.Node_JOIN {
   231  		joinTypeInfo, err := ndesc.GetJoinTypeInfo(ctx, options)
   232  		if err != nil {
   233  			return nil, err
   234  		}
   235  		lines = append(lines, joinTypeInfo)
   236  	}
   237  
   238  	// Get Join Condition info
   239  	if len(ndesc.Node.OnList) > 0 {
   240  		joinOnInfo, err := ndesc.GetJoinConditionInfo(ctx, options)
   241  		if err != nil {
   242  			return nil, err
   243  		}
   244  		lines = append(lines, joinOnInfo)
   245  	}
   246  
   247  	// Get Group key info
   248  	if len(ndesc.Node.GroupBy) > 0 {
   249  		groupByInfo, err := ndesc.GetGroupByInfo(ctx, options)
   250  		if err != nil {
   251  			return nil, err
   252  		}
   253  		lines = append(lines, groupByInfo)
   254  	}
   255  
   256  	// Get Aggregate function info
   257  	if len(ndesc.Node.AggList) > 0 {
   258  		aggListInfo, err := ndesc.GetAggregationInfo(ctx, options)
   259  		if err != nil {
   260  			return nil, err
   261  		}
   262  		lines = append(lines, aggListInfo)
   263  	}
   264  
   265  	// Get Filter list info
   266  	if len(ndesc.Node.FilterList) > 0 {
   267  		filterInfo, err := ndesc.GetFilterConditionInfo(ctx, options)
   268  		if err != nil {
   269  			return nil, err
   270  		}
   271  		lines = append(lines, filterInfo)
   272  	}
   273  
   274  	// Get Limit And Offset info
   275  	if ndesc.Node.Limit != nil {
   276  		var temp string
   277  		limitInfo, err := describeExpr(ctx, ndesc.Node.Limit, options)
   278  		if err != nil {
   279  			return nil, err
   280  		}
   281  		temp += "Limit: " + limitInfo
   282  		if ndesc.Node.Offset != nil {
   283  			offsetInfo, err := describeExpr(ctx, ndesc.Node.Offset, options)
   284  			if err != nil {
   285  				return nil, err
   286  			}
   287  			temp += ", Offset: " + offsetInfo
   288  		}
   289  		lines = append(lines, temp)
   290  	}
   291  
   292  	//if ndesc.Node.UpdateList != nil {
   293  	//	updateListDesc := &UpdateListDescribeImpl{
   294  	//		UpdateList: ndesc.Node.UpdateList,
   295  	//	}
   296  	//	updatedesc, err := updateListDesc.GetDescription(options)
   297  	//	if err != nil {
   298  	//		return nil, err
   299  	//	}
   300  	//	lines = append(lines, "Set columns with("+updatedesc+")")
   301  	//}
   302  	return lines, nil
   303  }
   304  
   305  func (ndesc *NodeDescribeImpl) GetProjectListInfo(ctx context.Context, options *ExplainOptions) (string, error) {
   306  	result := "Output: "
   307  	exprs := NewExprListDescribeImpl(ndesc.Node.ProjectList)
   308  	describe, err := exprs.GetDescription(ctx, options)
   309  	if err != nil {
   310  		return result, err
   311  	}
   312  	result += describe
   313  	return result, nil
   314  }
   315  
   316  func (ndesc *NodeDescribeImpl) GetJoinTypeInfo(ctx context.Context, options *ExplainOptions) (string, error) {
   317  	result := "Join Type: " + ndesc.Node.JoinType.String()
   318  	return result, nil
   319  }
   320  
   321  func (ndesc *NodeDescribeImpl) GetJoinConditionInfo(ctx context.Context, options *ExplainOptions) (string, error) {
   322  	result := "Join Cond: "
   323  	exprs := NewExprListDescribeImpl(ndesc.Node.OnList)
   324  	describe, err := exprs.GetDescription(ctx, options)
   325  	if err != nil {
   326  		return result, err
   327  	}
   328  	result += describe
   329  	return result, nil
   330  }
   331  
   332  func (ndesc *NodeDescribeImpl) GetFilterConditionInfo(ctx context.Context, options *ExplainOptions) (string, error) {
   333  	result := "Filter Cond: "
   334  	if options.Format == EXPLAIN_FORMAT_TEXT {
   335  		first := true
   336  		for _, v := range ndesc.Node.FilterList {
   337  			if !first {
   338  				result += ", "
   339  			}
   340  			first = false
   341  			descV, err := describeExpr(ctx, v, options)
   342  			if err != nil {
   343  				return result, err
   344  			}
   345  			result += descV
   346  		}
   347  	} else if options.Format == EXPLAIN_FORMAT_JSON {
   348  		return result, moerr.NewNYI(ctx, "explain format json")
   349  	} else if options.Format == EXPLAIN_FORMAT_DOT {
   350  		return result, moerr.NewNYI(ctx, "explain format dot")
   351  	}
   352  	return result, nil
   353  }
   354  
   355  func (ndesc *NodeDescribeImpl) GetGroupByInfo(ctx context.Context, options *ExplainOptions) (string, error) {
   356  	result := "Group Key: "
   357  	if options.Format == EXPLAIN_FORMAT_TEXT {
   358  		first := true
   359  		for _, v := range ndesc.Node.GetGroupBy() {
   360  			if !first {
   361  				result += ", "
   362  			}
   363  			first = false
   364  			descV, err := describeExpr(ctx, v, options)
   365  			if err != nil {
   366  				return result, err
   367  			}
   368  			result += descV
   369  		}
   370  	} else if options.Format == EXPLAIN_FORMAT_JSON {
   371  		return result, moerr.NewNYI(ctx, "explain format json")
   372  	} else if options.Format == EXPLAIN_FORMAT_DOT {
   373  		return result, moerr.NewNYI(ctx, "explain format dot")
   374  	}
   375  	return result, nil
   376  }
   377  
   378  func (ndesc *NodeDescribeImpl) GetAggregationInfo(ctx context.Context, options *ExplainOptions) (string, error) {
   379  	result := "Aggregate Functions: "
   380  	if options.Format == EXPLAIN_FORMAT_TEXT {
   381  		first := true
   382  		for _, v := range ndesc.Node.GetAggList() {
   383  			if !first {
   384  				result += ", "
   385  			}
   386  			first = false
   387  			descV, err := describeExpr(ctx, v, options)
   388  			if err != nil {
   389  				return result, err
   390  			}
   391  			result += descV
   392  		}
   393  	} else if options.Format == EXPLAIN_FORMAT_JSON {
   394  		return result, moerr.NewNYI(ctx, "explain format json")
   395  	} else if options.Format == EXPLAIN_FORMAT_DOT {
   396  		return result, moerr.NewNYI(ctx, "explain format dot")
   397  	}
   398  	return result, nil
   399  }
   400  
   401  func (ndesc *NodeDescribeImpl) GetOrderByInfo(ctx context.Context, options *ExplainOptions) (string, error) {
   402  	var result string
   403  	if options.Format == EXPLAIN_FORMAT_TEXT {
   404  		result = "Sort Key: "
   405  		orderByDescImpl := NewOrderByDescribeImpl(ndesc.Node.OrderBy)
   406  		describe, err := orderByDescImpl.GetDescription(ctx, options)
   407  		if err != nil {
   408  			return result, err
   409  		}
   410  		result += describe
   411  	} else if options.Format == EXPLAIN_FORMAT_JSON {
   412  		return result, moerr.NewNYI(ctx, "explain format json")
   413  	} else if options.Format == EXPLAIN_FORMAT_DOT {
   414  		return result, moerr.NewNYI(ctx, "explain format dot")
   415  	}
   416  	return result, nil
   417  }
   418  
   419  var _ NodeElemDescribe = &CostDescribeImpl{}
   420  var _ NodeElemDescribe = &ExprListDescribeImpl{}
   421  var _ NodeElemDescribe = &OrderByDescribeImpl{}
   422  var _ NodeElemDescribe = &WinSpecDescribeImpl{}
   423  var _ NodeElemDescribe = &RowsetDataDescribeImpl{}
   424  var _ NodeElemDescribe = &UpdateCtxsDescribeImpl{}
   425  var _ NodeElemDescribe = &AnalyzeInfoDescribeImpl{}
   426  
   427  type AnalyzeInfoDescribeImpl struct {
   428  	AnalyzeInfo *plan.AnalyzeInfo
   429  }
   430  
   431  func NewAnalyzeInfoDescribeImpl(analyze *plan.AnalyzeInfo) *AnalyzeInfoDescribeImpl {
   432  	return &AnalyzeInfoDescribeImpl{
   433  		AnalyzeInfo: analyze,
   434  	}
   435  }
   436  
   437  func (a AnalyzeInfoDescribeImpl) GetDescription(ctx context.Context, options *ExplainOptions) (string, error) {
   438  	result := "timeConsumed=" + strconv.FormatInt(a.AnalyzeInfo.TimeConsumed, 10) + "ns" +
   439  		" waitTime=" + strconv.FormatInt(a.AnalyzeInfo.WaitTimeConsumed, 10) + "ns" +
   440  		" inputRows=" + strconv.FormatInt(a.AnalyzeInfo.InputRows, 10) +
   441  		" outputRows=" + strconv.FormatInt(a.AnalyzeInfo.OutputRows, 10) +
   442  		" inputSize=" + strconv.FormatInt(a.AnalyzeInfo.InputSize, 10) + "bytes" +
   443  		" outputSize=" + strconv.FormatInt(a.AnalyzeInfo.OutputSize, 10) + "bytes" +
   444  		" memorySize=" + strconv.FormatInt(a.AnalyzeInfo.MemorySize, 10) + "bytes"
   445  	return result, nil
   446  }
   447  
   448  type CostDescribeImpl struct {
   449  	Stats *plan.Stats
   450  }
   451  
   452  func (c *CostDescribeImpl) GetDescription(ctx context.Context, options *ExplainOptions) (string, error) {
   453  	var result string
   454  	if c.Stats == nil {
   455  		result = " (cost=0)"
   456  		//result = " (cost=%.2f..%.2f rows=%.2f ndv=%.2f rowsize=%.f)"
   457  	} else {
   458  		var blockNumStr, hashmapSizeStr, selString string
   459  		if c.Stats.BlockNum > 0 {
   460  			blockNumStr = " blockNum=" + strconv.FormatInt(int64(c.Stats.BlockNum), 10)
   461  			selString = " selectivity=" + strconv.FormatFloat(c.Stats.Selectivity, 'f', 2, 64)
   462  		}
   463  		if c.Stats.HashmapSize > 0 {
   464  			hashmapSizeStr = " hashmapSize=" + strconv.FormatFloat(c.Stats.HashmapSize, 'f', 2, 64)
   465  		}
   466  
   467  		result = " (cost=" + strconv.FormatFloat(c.Stats.Cost, 'f', 2, 64) +
   468  			" outcnt=" + strconv.FormatFloat(c.Stats.Outcnt, 'f', 2, 64) +
   469  			blockNumStr + selString + hashmapSizeStr + ")"
   470  	}
   471  	return result, nil
   472  }
   473  
   474  type ExprListDescribeImpl struct {
   475  	ExprList []*plan.Expr // ProjectList,OnList,FilterList,GroupBy,GroupingSet and so on
   476  }
   477  
   478  func NewExprListDescribeImpl(ExprList []*plan.Expr) *ExprListDescribeImpl {
   479  	return &ExprListDescribeImpl{
   480  		ExprList: ExprList,
   481  	}
   482  }
   483  
   484  func (e *ExprListDescribeImpl) GetDescription(ctx context.Context, options *ExplainOptions) (string, error) {
   485  	first := true
   486  	var result string
   487  	if options.Format == EXPLAIN_FORMAT_TEXT {
   488  		for _, v := range e.ExprList {
   489  			if !first {
   490  				result += ", "
   491  			}
   492  			first = false
   493  			descV, err := describeExpr(ctx, v, options)
   494  			if err != nil {
   495  				return result, err
   496  			}
   497  			result += descV
   498  		}
   499  	} else if options.Format == EXPLAIN_FORMAT_JSON {
   500  		return result, moerr.NewNYI(ctx, "explain format json")
   501  	} else if options.Format == EXPLAIN_FORMAT_DOT {
   502  		return result, moerr.NewNYI(ctx, "explain format dot")
   503  	}
   504  	return result, nil
   505  }
   506  
   507  type OrderByDescribeImpl struct {
   508  	OrderBy []*plan.OrderBySpec
   509  }
   510  
   511  func NewOrderByDescribeImpl(OrderBy []*plan.OrderBySpec) *OrderByDescribeImpl {
   512  	return &OrderByDescribeImpl{
   513  		OrderBy: OrderBy,
   514  	}
   515  }
   516  
   517  func (o *OrderByDescribeImpl) GetDescription(ctx context.Context, options *ExplainOptions) (string, error) {
   518  	var result string
   519  	if options.Format == EXPLAIN_FORMAT_TEXT || options.Format == EXPLAIN_FORMAT_JSON {
   520  		first := true
   521  		for _, v := range o.OrderBy {
   522  			if !first {
   523  				result += ", "
   524  			}
   525  			first = false
   526  			descExpr, err := describeExpr(ctx, v.Expr, options)
   527  			if err != nil {
   528  				return result, err
   529  			}
   530  			result += descExpr
   531  
   532  			flagKey := int32(v.Flag)
   533  			orderbyFlag := plan.OrderBySpec_OrderByFlag_name[flagKey]
   534  			result += " " + orderbyFlag
   535  		}
   536  		return result, nil
   537  	} else if options.Format == EXPLAIN_FORMAT_DOT {
   538  		return "", moerr.NewNYI(ctx, "explain format dot")
   539  	}
   540  	return result, nil
   541  }
   542  
   543  type WinSpecDescribeImpl struct {
   544  	WinSpec *plan.WindowSpec
   545  }
   546  
   547  func (w *WinSpecDescribeImpl) GetDescription(ctx context.Context, options *ExplainOptions) (string, error) {
   548  	// TODO implement me
   549  	panic("implement me")
   550  }
   551  
   552  type RowsetDataDescribeImpl struct {
   553  	RowsetData *plan.RowsetData
   554  }
   555  
   556  func (r *RowsetDataDescribeImpl) GetDescription(ctx context.Context, options *ExplainOptions) (string, error) {
   557  	result := "Value:"
   558  	if r.RowsetData == nil {
   559  		return result, nil
   560  	}
   561  
   562  	first := true
   563  	for index := range r.RowsetData.Cols {
   564  		if !first {
   565  			result += ", "
   566  		}
   567  		first = false
   568  		result += "\"*VALUES*\".column" + strconv.Itoa(index+1)
   569  	}
   570  	return result, nil
   571  }
   572  
   573  type UpdateCtxsDescribeImpl struct {
   574  	UpdateCtx *plan.UpdateCtx
   575  }
   576  
   577  func (u *UpdateCtxsDescribeImpl) GetDescription(ctx context.Context, options *ExplainOptions) (string, error) {
   578  	result := "Update Columns: "
   579  	first := true
   580  	for i, ctx := range u.UpdateCtx.Ref {
   581  		if u.UpdateCtx.UpdateCol[i] != nil {
   582  			for colName := range u.UpdateCtx.UpdateCol[i].Map {
   583  				if !first {
   584  					result += ", "
   585  				} else {
   586  					first = false
   587  				}
   588  				result += ctx.SchemaName + "." + ctx.ObjName + "." + colName
   589  			}
   590  		}
   591  	}
   592  	return result, nil
   593  }