github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/petri/acyclic/causet/embedded/explain.go (about)

     1  // Copyright 2020 WHTCORPS INC, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package embedded
    15  
    16  import (
    17  	"bytes"
    18  	"fmt"
    19  	"strconv"
    20  	"strings"
    21  
    22  	"github.com/whtcorpsinc/BerolinaSQL/ast"
    23  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    24  	"github.com/whtcorpsinc/milevadb/causet"
    25  	"github.com/whtcorpsinc/milevadb/causet/soliton"
    26  	"github.com/whtcorpsinc/milevadb/memex"
    27  	"github.com/whtcorpsinc/milevadb/memex/aggregation"
    28  	"github.com/whtcorpsinc/milevadb/schemareplicant"
    29  	"github.com/whtcorpsinc/milevadb/soliton/stringutil"
    30  	"github.com/whtcorpsinc/milevadb/statistics"
    31  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    32  )
    33  
    34  // A plan is dataAccesser means it can access underlying data.
    35  // Include `PhysicalBlockScan`, `PhysicalIndexScan`, `PointGetCauset`, `BatchPointScan` and `PhysicalMemBlock`.
    36  // ExplainInfo = AccessObject + OperatorInfo
    37  type dataAccesser interface {
    38  
    39  	// AccessObject return plan's `causet`, `partition` and `index`.
    40  	AccessObject() string
    41  
    42  	// OperatorInfo return other operator information to be explained.
    43  	OperatorInfo(normalized bool) string
    44  }
    45  
    46  type partitionAccesser interface {
    47  	accessObject(stochastikctx.Context) string
    48  }
    49  
    50  // ExplainInfo implements Causet interface.
    51  func (p *PhysicalLock) ExplainInfo() string {
    52  	return fmt.Sprintf("%s %v", p.Lock.LockType.String(), p.Lock.WaitSec)
    53  }
    54  
    55  // ExplainID overrides the ExplainID in order to match different range.
    56  func (p *PhysicalIndexScan) ExplainID() fmt.Stringer {
    57  	return stringutil.MemoizeStr(func() string {
    58  		if p.isFullScan() {
    59  			return "IndexFullScan_" + strconv.Itoa(p.id)
    60  		}
    61  		return "IndexRangeScan_" + strconv.Itoa(p.id)
    62  	})
    63  }
    64  
    65  // ExplainInfo implements Causet interface.
    66  func (p *PhysicalIndexScan) ExplainInfo() string {
    67  	return p.AccessObject() + ", " + p.OperatorInfo(false)
    68  }
    69  
    70  // ExplainNormalizedInfo implements Causet interface.
    71  func (p *PhysicalIndexScan) ExplainNormalizedInfo() string {
    72  	return p.AccessObject() + ", " + p.OperatorInfo(true)
    73  }
    74  
    75  // AccessObject implements dataAccesser interface.
    76  func (p *PhysicalIndexScan) AccessObject() string {
    77  	buffer := bytes.NewBufferString("")
    78  	tblName := p.Block.Name.O
    79  	if p.BlockAsName != nil && p.BlockAsName.O != "" {
    80  		tblName = p.BlockAsName.O
    81  	}
    82  	fmt.Fprintf(buffer, "causet:%s", tblName)
    83  	if p.isPartition {
    84  		if pi := p.Block.GetPartitionInfo(); pi != nil {
    85  			partitionName := pi.GetNameByID(p.physicalBlockID)
    86  			fmt.Fprintf(buffer, ", partition:%s", partitionName)
    87  		}
    88  	}
    89  	if len(p.Index.DeferredCausets) > 0 {
    90  		buffer.WriteString(", index:" + p.Index.Name.O + "(")
    91  		for i, idxDefCaus := range p.Index.DeferredCausets {
    92  			buffer.WriteString(idxDefCaus.Name.O)
    93  			if i+1 < len(p.Index.DeferredCausets) {
    94  				buffer.WriteString(", ")
    95  			}
    96  		}
    97  		buffer.WriteString(")")
    98  	}
    99  	return buffer.String()
   100  }
   101  
   102  // OperatorInfo implements dataAccesser interface.
   103  func (p *PhysicalIndexScan) OperatorInfo(normalized bool) string {
   104  	buffer := bytes.NewBufferString("")
   105  	if len(p.rangeInfo) > 0 {
   106  		if !normalized {
   107  			fmt.Fprintf(buffer, "range: decided by %v, ", p.rangeInfo)
   108  		}
   109  	} else if p.haveCorDefCaus() {
   110  		if normalized {
   111  			fmt.Fprintf(buffer, "range: decided by %s, ", memex.SortedExplainNormalizedExpressionList(p.AccessCondition))
   112  		} else {
   113  			fmt.Fprintf(buffer, "range: decided by %v, ", p.AccessCondition)
   114  		}
   115  	} else if len(p.Ranges) > 0 {
   116  		if normalized {
   117  			fmt.Fprint(buffer, "range:[?,?], ")
   118  		} else if !p.isFullScan() {
   119  			fmt.Fprint(buffer, "range:")
   120  			for _, idxRange := range p.Ranges {
   121  				fmt.Fprint(buffer, idxRange.String()+", ")
   122  			}
   123  		}
   124  	}
   125  	fmt.Fprintf(buffer, "keep order:%v, ", p.KeepOrder)
   126  	if p.Desc {
   127  		buffer.WriteString("desc, ")
   128  	}
   129  	if p.stats.StatsVersion == statistics.PseudoVersion && !normalized {
   130  		buffer.WriteString("stats:pseudo, ")
   131  	}
   132  	buffer.Truncate(buffer.Len() - 2)
   133  	return buffer.String()
   134  }
   135  
   136  func (p *PhysicalIndexScan) haveCorDefCaus() bool {
   137  	for _, cond := range p.AccessCondition {
   138  		if len(memex.ExtractCorDeferredCausets(cond)) > 0 {
   139  			return true
   140  		}
   141  	}
   142  	return false
   143  }
   144  
   145  func (p *PhysicalIndexScan) isFullScan() bool {
   146  	if len(p.rangeInfo) > 0 || p.haveCorDefCaus() {
   147  		return false
   148  	}
   149  	for _, ran := range p.Ranges {
   150  		if !ran.IsFullRange() {
   151  			return false
   152  		}
   153  	}
   154  	return true
   155  }
   156  
   157  // ExplainID overrides the ExplainID in order to match different range.
   158  func (p *PhysicalBlockScan) ExplainID() fmt.Stringer {
   159  	return stringutil.MemoizeStr(func() string {
   160  		if p.isChildOfIndexLookUp {
   161  			return "BlockRowIDScan_" + strconv.Itoa(p.id)
   162  		} else if p.isFullScan() {
   163  			return "BlockFullScan_" + strconv.Itoa(p.id)
   164  		}
   165  		return "BlockRangeScan_" + strconv.Itoa(p.id)
   166  	})
   167  }
   168  
   169  // ExplainInfo implements Causet interface.
   170  func (p *PhysicalBlockScan) ExplainInfo() string {
   171  	return p.AccessObject() + ", " + p.OperatorInfo(false)
   172  }
   173  
   174  // ExplainNormalizedInfo implements Causet interface.
   175  func (p *PhysicalBlockScan) ExplainNormalizedInfo() string {
   176  	return p.AccessObject() + ", " + p.OperatorInfo(true)
   177  }
   178  
   179  // AccessObject implements dataAccesser interface.
   180  func (p *PhysicalBlockScan) AccessObject() string {
   181  	buffer := bytes.NewBufferString("")
   182  	tblName := p.Block.Name.O
   183  	if p.BlockAsName != nil && p.BlockAsName.O != "" {
   184  		tblName = p.BlockAsName.O
   185  	}
   186  	fmt.Fprintf(buffer, "causet:%s", tblName)
   187  	if p.isPartition {
   188  		if pi := p.Block.GetPartitionInfo(); pi != nil {
   189  			partitionName := pi.GetNameByID(p.physicalBlockID)
   190  			fmt.Fprintf(buffer, ", partition:%s", partitionName)
   191  		}
   192  	}
   193  	return buffer.String()
   194  }
   195  
   196  // OperatorInfo implements dataAccesser interface.
   197  func (p *PhysicalBlockScan) OperatorInfo(normalized bool) string {
   198  	buffer := bytes.NewBufferString("")
   199  	for i, pkDefCaus := range p.PkDefCauss {
   200  		var fmtStr string
   201  		switch i {
   202  		case 0:
   203  			fmtStr = "pk defcaus: (%s, "
   204  		case len(p.PkDefCauss) - 1:
   205  			fmtStr = "%s)"
   206  		default:
   207  			fmtStr = "%s, "
   208  		}
   209  		fmt.Fprintf(buffer, fmtStr, pkDefCaus.ExplainInfo())
   210  	}
   211  	if len(p.rangeDecidedBy) > 0 {
   212  		fmt.Fprintf(buffer, "range: decided by %v, ", p.rangeDecidedBy)
   213  	} else if p.haveCorDefCaus() {
   214  		if normalized {
   215  			fmt.Fprintf(buffer, "range: decided by %s, ", memex.SortedExplainNormalizedExpressionList(p.AccessCondition))
   216  		} else {
   217  			fmt.Fprintf(buffer, "range: decided by %v, ", p.AccessCondition)
   218  		}
   219  	} else if len(p.Ranges) > 0 {
   220  		if normalized {
   221  			fmt.Fprint(buffer, "range:[?,?], ")
   222  		} else if !p.isFullScan() {
   223  			fmt.Fprint(buffer, "range:")
   224  			for _, idxRange := range p.Ranges {
   225  				fmt.Fprint(buffer, idxRange.String()+", ")
   226  			}
   227  		}
   228  	}
   229  	fmt.Fprintf(buffer, "keep order:%v, ", p.KeepOrder)
   230  	if p.Desc {
   231  		buffer.WriteString("desc, ")
   232  	}
   233  	if p.stats.StatsVersion == statistics.PseudoVersion && !normalized {
   234  		buffer.WriteString("stats:pseudo, ")
   235  	}
   236  	if p.IsGlobalRead {
   237  		buffer.WriteString("global read, ")
   238  	}
   239  	buffer.Truncate(buffer.Len() - 2)
   240  	return buffer.String()
   241  }
   242  
   243  func (p *PhysicalBlockScan) haveCorDefCaus() bool {
   244  	for _, cond := range p.AccessCondition {
   245  		if len(memex.ExtractCorDeferredCausets(cond)) > 0 {
   246  			return true
   247  		}
   248  	}
   249  	return false
   250  }
   251  
   252  func (p *PhysicalBlockScan) isFullScan() bool {
   253  	if len(p.rangeDecidedBy) > 0 || p.haveCorDefCaus() {
   254  		return false
   255  	}
   256  	for _, ran := range p.Ranges {
   257  		if !ran.IsFullRange() {
   258  			return false
   259  		}
   260  	}
   261  	return true
   262  }
   263  
   264  // ExplainInfo implements Causet interface.
   265  func (p *PhysicalBlockReader) ExplainInfo() string {
   266  	return "data:" + p.blockCauset.ExplainID().String()
   267  }
   268  
   269  // ExplainNormalizedInfo implements Causet interface.
   270  func (p *PhysicalBlockReader) ExplainNormalizedInfo() string {
   271  	return ""
   272  }
   273  
   274  func (p *PhysicalBlockReader) accessObject(sctx stochastikctx.Context) string {
   275  	ts := p.BlockCausets[0].(*PhysicalBlockScan)
   276  	pi := ts.Block.GetPartitionInfo()
   277  	if pi == nil || !sctx.GetStochastikVars().UseDynamicPartitionPrune() {
   278  		return ""
   279  	}
   280  
   281  	is := schemareplicant.GetSchemaReplicant(sctx)
   282  	tmp, ok := is.BlockByID(ts.Block.ID)
   283  	if !ok {
   284  		return "partition causet not found" + strconv.FormatInt(ts.Block.ID, 10)
   285  	}
   286  	tbl := tmp.(causet.PartitionedBlock)
   287  
   288  	return partitionAccessObject(sctx, tbl, pi, &p.PartitionInfo)
   289  }
   290  
   291  func partitionAccessObject(sctx stochastikctx.Context, tbl causet.PartitionedBlock, pi *perceptron.PartitionInfo, partBlock *PartitionInfo) string {
   292  	var buffer bytes.Buffer
   293  	idxArr, err := PartitionPruning(sctx, tbl, partBlock.PruningConds, partBlock.PartitionNames, partBlock.DeferredCausets, partBlock.DeferredCausetNames)
   294  	if err != nil {
   295  		return "partition pruning error" + err.Error()
   296  	}
   297  
   298  	if len(idxArr) == 0 {
   299  		return "partition:dual"
   300  	}
   301  
   302  	if len(idxArr) == 1 && idxArr[0] == FullRange {
   303  		return "partition:all"
   304  	}
   305  
   306  	for i, idx := range idxArr {
   307  		if i == 0 {
   308  			buffer.WriteString("partition:")
   309  		} else {
   310  			buffer.WriteString(",")
   311  		}
   312  		buffer.WriteString(pi.Definitions[idx].Name.O)
   313  	}
   314  
   315  	return buffer.String()
   316  }
   317  
   318  // OperatorInfo return other operator information to be explained.
   319  func (p *PhysicalBlockReader) OperatorInfo(normalized bool) string {
   320  	return "data:" + p.blockCauset.ExplainID().String()
   321  }
   322  
   323  // ExplainInfo implements Causet interface.
   324  func (p *PhysicalIndexReader) ExplainInfo() string {
   325  	return "index:" + p.indexCauset.ExplainID().String()
   326  }
   327  
   328  // ExplainNormalizedInfo implements Causet interface.
   329  func (p *PhysicalIndexReader) ExplainNormalizedInfo() string {
   330  	return p.ExplainInfo()
   331  }
   332  
   333  func (p *PhysicalIndexReader) accessObject(sctx stochastikctx.Context) string {
   334  	ts := p.IndexCausets[0].(*PhysicalIndexScan)
   335  	pi := ts.Block.GetPartitionInfo()
   336  	if pi == nil || !sctx.GetStochastikVars().UseDynamicPartitionPrune() {
   337  		return ""
   338  	}
   339  
   340  	var buffer bytes.Buffer
   341  	is := schemareplicant.GetSchemaReplicant(sctx)
   342  	tmp, ok := is.BlockByID(ts.Block.ID)
   343  	if !ok {
   344  		fmt.Fprintf(&buffer, "partition causet not found: %d", ts.Block.ID)
   345  		return buffer.String()
   346  	}
   347  
   348  	tbl := tmp.(causet.PartitionedBlock)
   349  	return partitionAccessObject(sctx, tbl, pi, &p.PartitionInfo)
   350  }
   351  
   352  // ExplainInfo implements Causet interface.
   353  func (p *PhysicalIndexLookUpReader) ExplainInfo() string {
   354  	// The children can be inferred by the relation symbol.
   355  	if p.PushedLimit != nil {
   356  		return fmt.Sprintf("limit embedded(offset:%v, count:%v)", p.PushedLimit.Offset, p.PushedLimit.Count)
   357  	}
   358  	return ""
   359  }
   360  
   361  func (p *PhysicalIndexLookUpReader) accessObject(sctx stochastikctx.Context) string {
   362  	ts := p.BlockCausets[0].(*PhysicalBlockScan)
   363  	pi := ts.Block.GetPartitionInfo()
   364  	if pi == nil || !sctx.GetStochastikVars().UseDynamicPartitionPrune() {
   365  		return ""
   366  	}
   367  
   368  	var buffer bytes.Buffer
   369  	is := schemareplicant.GetSchemaReplicant(sctx)
   370  	tmp, ok := is.BlockByID(ts.Block.ID)
   371  	if !ok {
   372  		fmt.Fprintf(&buffer, "partition causet not found: %d", ts.Block.ID)
   373  		return buffer.String()
   374  	}
   375  
   376  	tbl := tmp.(causet.PartitionedBlock)
   377  	return partitionAccessObject(sctx, tbl, pi, &p.PartitionInfo)
   378  }
   379  
   380  // ExplainInfo implements Causet interface.
   381  func (p *PhysicalIndexMergeReader) ExplainInfo() string {
   382  	return ""
   383  }
   384  
   385  func (p *PhysicalIndexMergeReader) accessObject(sctx stochastikctx.Context) string {
   386  	ts := p.BlockCausets[0].(*PhysicalBlockScan)
   387  	pi := ts.Block.GetPartitionInfo()
   388  	if pi == nil || !sctx.GetStochastikVars().UseDynamicPartitionPrune() {
   389  		return ""
   390  	}
   391  
   392  	is := schemareplicant.GetSchemaReplicant(sctx)
   393  	tmp, ok := is.BlockByID(ts.Block.ID)
   394  	if !ok {
   395  		return "partition causet not found" + strconv.FormatInt(ts.Block.ID, 10)
   396  	}
   397  	tbl := tmp.(causet.PartitionedBlock)
   398  
   399  	return partitionAccessObject(sctx, tbl, pi, &p.PartitionInfo)
   400  }
   401  
   402  // ExplainInfo implements Causet interface.
   403  func (p *PhysicalUnionScan) ExplainInfo() string {
   404  	return string(memex.SortedExplainExpressionList(p.Conditions))
   405  }
   406  
   407  // ExplainInfo implements Causet interface.
   408  func (p *PhysicalSelection) ExplainInfo() string {
   409  	return string(memex.SortedExplainExpressionList(p.Conditions))
   410  }
   411  
   412  // ExplainNormalizedInfo implements Causet interface.
   413  func (p *PhysicalSelection) ExplainNormalizedInfo() string {
   414  	return string(memex.SortedExplainNormalizedExpressionList(p.Conditions))
   415  }
   416  
   417  // ExplainInfo implements Causet interface.
   418  func (p *PhysicalProjection) ExplainInfo() string {
   419  	return memex.ExplainExpressionList(p.Exprs, p.schemaReplicant)
   420  }
   421  
   422  // ExplainNormalizedInfo implements Causet interface.
   423  func (p *PhysicalProjection) ExplainNormalizedInfo() string {
   424  	return string(memex.SortedExplainNormalizedExpressionList(p.Exprs))
   425  }
   426  
   427  // ExplainInfo implements Causet interface.
   428  func (p *PhysicalBlockDual) ExplainInfo() string {
   429  	return fmt.Sprintf("rows:%v", p.RowCount)
   430  }
   431  
   432  // ExplainInfo implements Causet interface.
   433  func (p *PhysicalSort) ExplainInfo() string {
   434  	buffer := bytes.NewBufferString("")
   435  	return explainByItems(buffer, p.ByItems).String()
   436  }
   437  
   438  // ExplainInfo implements Causet interface.
   439  func (p *PhysicalLimit) ExplainInfo() string {
   440  	return fmt.Sprintf("offset:%v, count:%v", p.Offset, p.Count)
   441  }
   442  
   443  // ExplainInfo implements Causet interface.
   444  func (p *basePhysicalAgg) ExplainInfo() string {
   445  	return p.explainInfo(false)
   446  }
   447  
   448  func (p *basePhysicalAgg) explainInfo(normalized bool) string {
   449  	sortedExplainExpressionList := memex.SortedExplainExpressionList
   450  	if normalized {
   451  		sortedExplainExpressionList = memex.SortedExplainNormalizedExpressionList
   452  	}
   453  
   454  	builder := &strings.Builder{}
   455  	if len(p.GroupByItems) > 0 {
   456  		fmt.Fprintf(builder, "group by:%s, ",
   457  			sortedExplainExpressionList(p.GroupByItems))
   458  	}
   459  	for i := 0; i < len(p.AggFuncs); i++ {
   460  		builder.WriteString("funcs:")
   461  		var colName string
   462  		if normalized {
   463  			colName = p.schemaReplicant.DeferredCausets[i].ExplainNormalizedInfo()
   464  		} else {
   465  			colName = p.schemaReplicant.DeferredCausets[i].ExplainInfo()
   466  		}
   467  		fmt.Fprintf(builder, "%v->%v", aggregation.ExplainAggFunc(p.AggFuncs[i], normalized), colName)
   468  		if i+1 < len(p.AggFuncs) {
   469  			builder.WriteString(", ")
   470  		}
   471  	}
   472  	return builder.String()
   473  }
   474  
   475  // ExplainNormalizedInfo implements Causet interface.
   476  func (p *basePhysicalAgg) ExplainNormalizedInfo() string {
   477  	return p.explainInfo(true)
   478  }
   479  
   480  // ExplainInfo implements Causet interface.
   481  func (p *PhysicalIndexJoin) ExplainInfo() string {
   482  	return p.explainInfo(false)
   483  }
   484  
   485  func (p *PhysicalIndexJoin) explainInfo(normalized bool) string {
   486  	sortedExplainExpressionList := memex.SortedExplainExpressionList
   487  	if normalized {
   488  		sortedExplainExpressionList = memex.SortedExplainNormalizedExpressionList
   489  	}
   490  
   491  	buffer := bytes.NewBufferString(p.JoinType.String())
   492  	if normalized {
   493  		fmt.Fprintf(buffer, ", inner:%s", p.Children()[p.InnerChildIdx].TP())
   494  	} else {
   495  		fmt.Fprintf(buffer, ", inner:%s", p.Children()[p.InnerChildIdx].ExplainID())
   496  	}
   497  	if len(p.OuterJoinKeys) > 0 {
   498  		fmt.Fprintf(buffer, ", outer key:%s",
   499  			memex.ExplainDeferredCausetList(p.OuterJoinKeys))
   500  	}
   501  	if len(p.InnerJoinKeys) > 0 {
   502  		fmt.Fprintf(buffer, ", inner key:%s",
   503  			memex.ExplainDeferredCausetList(p.InnerJoinKeys))
   504  	}
   505  	if len(p.LeftConditions) > 0 {
   506  		fmt.Fprintf(buffer, ", left cond:%s",
   507  			sortedExplainExpressionList(p.LeftConditions))
   508  	}
   509  	if len(p.RightConditions) > 0 {
   510  		fmt.Fprintf(buffer, ", right cond:%s",
   511  			sortedExplainExpressionList(p.RightConditions))
   512  	}
   513  	if len(p.OtherConditions) > 0 {
   514  		fmt.Fprintf(buffer, ", other cond:%s",
   515  			sortedExplainExpressionList(p.OtherConditions))
   516  	}
   517  	return buffer.String()
   518  }
   519  
   520  // ExplainNormalizedInfo implements Causet interface.
   521  func (p *PhysicalIndexJoin) ExplainNormalizedInfo() string {
   522  	return p.explainInfo(true)
   523  }
   524  
   525  // ExplainInfo implements Causet interface.
   526  func (p *PhysicalHashJoin) ExplainInfo() string {
   527  	return p.explainInfo(false)
   528  }
   529  
   530  // ExplainNormalizedInfo implements Causet interface.
   531  func (p *PhysicalHashJoin) ExplainNormalizedInfo() string {
   532  	return p.explainInfo(true)
   533  }
   534  
   535  func (p *PhysicalHashJoin) explainInfo(normalized bool) string {
   536  	sortedExplainExpressionList := memex.SortedExplainExpressionList
   537  	if normalized {
   538  		sortedExplainExpressionList = memex.SortedExplainNormalizedExpressionList
   539  	}
   540  
   541  	buffer := new(bytes.Buffer)
   542  
   543  	if len(p.EqualConditions) == 0 {
   544  		buffer.WriteString("CARTESIAN ")
   545  	}
   546  
   547  	buffer.WriteString(p.JoinType.String())
   548  
   549  	if len(p.EqualConditions) > 0 {
   550  		if normalized {
   551  			fmt.Fprintf(buffer, ", equal:%s", memex.SortedExplainNormalizedScalarFuncList(p.EqualConditions))
   552  		} else {
   553  			fmt.Fprintf(buffer, ", equal:%v", p.EqualConditions)
   554  		}
   555  	}
   556  	if len(p.LeftConditions) > 0 {
   557  		if normalized {
   558  			fmt.Fprintf(buffer, ", left cond:%s", memex.SortedExplainNormalizedExpressionList(p.LeftConditions))
   559  		} else {
   560  			fmt.Fprintf(buffer, ", left cond:%s", p.LeftConditions)
   561  		}
   562  	}
   563  	if len(p.RightConditions) > 0 {
   564  		fmt.Fprintf(buffer, ", right cond:%s",
   565  			sortedExplainExpressionList(p.RightConditions))
   566  	}
   567  	if len(p.OtherConditions) > 0 {
   568  		fmt.Fprintf(buffer, ", other cond:%s",
   569  			sortedExplainExpressionList(p.OtherConditions))
   570  	}
   571  	return buffer.String()
   572  }
   573  
   574  // ExplainInfo implements Causet interface.
   575  func (p *PhysicalMergeJoin) ExplainInfo() string {
   576  	return p.explainInfo(false)
   577  }
   578  
   579  func (p *PhysicalMergeJoin) explainInfo(normalized bool) string {
   580  	sortedExplainExpressionList := memex.SortedExplainExpressionList
   581  	if normalized {
   582  		sortedExplainExpressionList = memex.SortedExplainNormalizedExpressionList
   583  	}
   584  
   585  	buffer := bytes.NewBufferString(p.JoinType.String())
   586  	if len(p.LeftJoinKeys) > 0 {
   587  		fmt.Fprintf(buffer, ", left key:%s",
   588  			memex.ExplainDeferredCausetList(p.LeftJoinKeys))
   589  	}
   590  	if len(p.RightJoinKeys) > 0 {
   591  		fmt.Fprintf(buffer, ", right key:%s",
   592  			memex.ExplainDeferredCausetList(p.RightJoinKeys))
   593  	}
   594  	if len(p.LeftConditions) > 0 {
   595  		if normalized {
   596  			fmt.Fprintf(buffer, ", left cond:%s", memex.SortedExplainNormalizedExpressionList(p.LeftConditions))
   597  		} else {
   598  			fmt.Fprintf(buffer, ", left cond:%s", p.LeftConditions)
   599  		}
   600  	}
   601  	if len(p.RightConditions) > 0 {
   602  		fmt.Fprintf(buffer, ", right cond:%s",
   603  			sortedExplainExpressionList(p.RightConditions))
   604  	}
   605  	if len(p.OtherConditions) > 0 {
   606  		fmt.Fprintf(buffer, ", other cond:%s",
   607  			sortedExplainExpressionList(p.OtherConditions))
   608  	}
   609  	return buffer.String()
   610  }
   611  
   612  // ExplainNormalizedInfo implements Causet interface.
   613  func (p *PhysicalMergeJoin) ExplainNormalizedInfo() string {
   614  	return p.explainInfo(true)
   615  }
   616  
   617  // ExplainInfo implements Causet interface.
   618  func (p *PhysicalBroadCastJoin) ExplainInfo() string {
   619  	return p.explainInfo()
   620  }
   621  
   622  // ExplainNormalizedInfo implements Causet interface.
   623  func (p *PhysicalBroadCastJoin) ExplainNormalizedInfo() string {
   624  	return p.explainInfo()
   625  }
   626  
   627  func (p *PhysicalBroadCastJoin) explainInfo() string {
   628  	buffer := new(bytes.Buffer)
   629  
   630  	buffer.WriteString(p.JoinType.String())
   631  
   632  	if len(p.LeftJoinKeys) > 0 {
   633  		fmt.Fprintf(buffer, ", left key:%s",
   634  			memex.ExplainDeferredCausetList(p.LeftJoinKeys))
   635  	}
   636  	if len(p.RightJoinKeys) > 0 {
   637  		fmt.Fprintf(buffer, ", right key:%s",
   638  			memex.ExplainDeferredCausetList(p.RightJoinKeys))
   639  	}
   640  	return buffer.String()
   641  }
   642  
   643  // ExplainInfo implements Causet interface.
   644  func (p *PhysicalTopN) ExplainInfo() string {
   645  	buffer := bytes.NewBufferString("")
   646  	buffer = explainByItems(buffer, p.ByItems)
   647  	fmt.Fprintf(buffer, ", offset:%v, count:%v", p.Offset, p.Count)
   648  	return buffer.String()
   649  }
   650  
   651  // ExplainNormalizedInfo implements Causet interface.
   652  func (p *PhysicalTopN) ExplainNormalizedInfo() string {
   653  	buffer := bytes.NewBufferString("")
   654  	buffer = explainNormalizedByItems(buffer, p.ByItems)
   655  	return buffer.String()
   656  }
   657  
   658  func (p *PhysicalWindow) formatFrameBound(buffer *bytes.Buffer, bound *FrameBound) {
   659  	if bound.Type == ast.CurrentRow {
   660  		buffer.WriteString("current event")
   661  		return
   662  	}
   663  	if bound.UnBounded {
   664  		buffer.WriteString("unbounded")
   665  	} else if len(bound.CalcFuncs) > 0 {
   666  		sf := bound.CalcFuncs[0].(*memex.ScalarFunction)
   667  		switch sf.FuncName.L {
   668  		case ast.DateAdd, ast.DateSub:
   669  			// For `interval '2:30' minute_second`.
   670  			fmt.Fprintf(buffer, "interval %s %s", sf.GetArgs()[1].ExplainInfo(), sf.GetArgs()[2].ExplainInfo())
   671  		case ast.Plus, ast.Minus:
   672  			// For `1 preceding` of range frame.
   673  			fmt.Fprintf(buffer, "%s", sf.GetArgs()[1].ExplainInfo())
   674  		}
   675  	} else {
   676  		fmt.Fprintf(buffer, "%d", bound.Num)
   677  	}
   678  	if bound.Type == ast.Preceding {
   679  		buffer.WriteString(" preceding")
   680  	} else {
   681  		buffer.WriteString(" following")
   682  	}
   683  }
   684  
   685  // ExplainInfo implements Causet interface.
   686  func (p *PhysicalWindow) ExplainInfo() string {
   687  	buffer := bytes.NewBufferString("")
   688  	formatWindowFuncDescs(buffer, p.WindowFuncDescs, p.schemaReplicant)
   689  	buffer.WriteString(" over(")
   690  	isFirst := true
   691  	if len(p.PartitionBy) > 0 {
   692  		buffer.WriteString("partition by ")
   693  		for i, item := range p.PartitionBy {
   694  			fmt.Fprintf(buffer, "%s", item.DefCaus.ExplainInfo())
   695  			if i+1 < len(p.PartitionBy) {
   696  				buffer.WriteString(", ")
   697  			}
   698  		}
   699  		isFirst = false
   700  	}
   701  	if len(p.OrderBy) > 0 {
   702  		if !isFirst {
   703  			buffer.WriteString(" ")
   704  		}
   705  		buffer.WriteString("order by ")
   706  		for i, item := range p.OrderBy {
   707  			if item.Desc {
   708  				fmt.Fprintf(buffer, "%s desc", item.DefCaus.ExplainInfo())
   709  			} else {
   710  				fmt.Fprintf(buffer, "%s", item.DefCaus.ExplainInfo())
   711  			}
   712  
   713  			if i+1 < len(p.OrderBy) {
   714  				buffer.WriteString(", ")
   715  			}
   716  		}
   717  		isFirst = false
   718  	}
   719  	if p.Frame != nil {
   720  		if !isFirst {
   721  			buffer.WriteString(" ")
   722  		}
   723  		if p.Frame.Type == ast.Rows {
   724  			buffer.WriteString("rows")
   725  		} else {
   726  			buffer.WriteString("range")
   727  		}
   728  		buffer.WriteString(" between ")
   729  		p.formatFrameBound(buffer, p.Frame.Start)
   730  		buffer.WriteString(" and ")
   731  		p.formatFrameBound(buffer, p.Frame.End)
   732  	}
   733  	buffer.WriteString(")")
   734  	return buffer.String()
   735  }
   736  
   737  // ExplainInfo implements Causet interface.
   738  func (p *PhysicalShuffle) ExplainInfo() string {
   739  	buffer := bytes.NewBufferString("")
   740  	fmt.Fprintf(buffer, "execution info: concurrency:%v, data source:%v", p.Concurrency, p.DataSource.ExplainID())
   741  	return buffer.String()
   742  }
   743  
   744  func formatWindowFuncDescs(buffer *bytes.Buffer, descs []*aggregation.WindowFuncDesc, schemaReplicant *memex.Schema) *bytes.Buffer {
   745  	winFuncStartIdx := len(schemaReplicant.DeferredCausets) - len(descs)
   746  	for i, desc := range descs {
   747  		if i != 0 {
   748  			buffer.WriteString(", ")
   749  		}
   750  		fmt.Fprintf(buffer, "%v->%v", desc, schemaReplicant.DeferredCausets[winFuncStartIdx+i])
   751  	}
   752  	return buffer
   753  }
   754  
   755  // ExplainInfo implements Causet interface.
   756  func (p *LogicalJoin) ExplainInfo() string {
   757  	buffer := bytes.NewBufferString(p.JoinType.String())
   758  	if len(p.EqualConditions) > 0 {
   759  		fmt.Fprintf(buffer, ", equal:%v", p.EqualConditions)
   760  	}
   761  	if len(p.LeftConditions) > 0 {
   762  		fmt.Fprintf(buffer, ", left cond:%s",
   763  			memex.SortedExplainExpressionList(p.LeftConditions))
   764  	}
   765  	if len(p.RightConditions) > 0 {
   766  		fmt.Fprintf(buffer, ", right cond:%s",
   767  			memex.SortedExplainExpressionList(p.RightConditions))
   768  	}
   769  	if len(p.OtherConditions) > 0 {
   770  		fmt.Fprintf(buffer, ", other cond:%s",
   771  			memex.SortedExplainExpressionList(p.OtherConditions))
   772  	}
   773  	return buffer.String()
   774  }
   775  
   776  // ExplainInfo implements Causet interface.
   777  func (p *LogicalAggregation) ExplainInfo() string {
   778  	buffer := bytes.NewBufferString("")
   779  	if len(p.GroupByItems) > 0 {
   780  		fmt.Fprintf(buffer, "group by:%s, ",
   781  			memex.SortedExplainExpressionList(p.GroupByItems))
   782  	}
   783  	if len(p.AggFuncs) > 0 {
   784  		buffer.WriteString("funcs:")
   785  		for i, agg := range p.AggFuncs {
   786  			buffer.WriteString(aggregation.ExplainAggFunc(agg, false))
   787  			if i+1 < len(p.AggFuncs) {
   788  				buffer.WriteString(", ")
   789  			}
   790  		}
   791  	}
   792  	return buffer.String()
   793  }
   794  
   795  // ExplainInfo implements Causet interface.
   796  func (p *LogicalProjection) ExplainInfo() string {
   797  	return memex.ExplainExpressionList(p.Exprs, p.schemaReplicant)
   798  }
   799  
   800  // ExplainInfo implements Causet interface.
   801  func (p *LogicalSelection) ExplainInfo() string {
   802  	return string(memex.SortedExplainExpressionList(p.Conditions))
   803  }
   804  
   805  // ExplainInfo implements Causet interface.
   806  func (p *LogicalApply) ExplainInfo() string {
   807  	return p.LogicalJoin.ExplainInfo()
   808  }
   809  
   810  // ExplainInfo implements Causet interface.
   811  func (p *LogicalBlockDual) ExplainInfo() string {
   812  	return fmt.Sprintf("rowcount:%d", p.RowCount)
   813  }
   814  
   815  // ExplainInfo implements Causet interface.
   816  func (p *DataSource) ExplainInfo() string {
   817  	buffer := bytes.NewBufferString("")
   818  	tblName := p.blockInfo.Name.O
   819  	if p.BlockAsName != nil && p.BlockAsName.O != "" {
   820  		tblName = p.BlockAsName.O
   821  	}
   822  	fmt.Fprintf(buffer, "causet:%s", tblName)
   823  	if p.isPartition {
   824  		if pi := p.blockInfo.GetPartitionInfo(); pi != nil {
   825  			partitionName := pi.GetNameByID(p.physicalBlockID)
   826  			fmt.Fprintf(buffer, ", partition:%s", partitionName)
   827  		}
   828  	}
   829  	return buffer.String()
   830  }
   831  
   832  // ExplainInfo implements Causet interface.
   833  func (p *LogicalUnionScan) ExplainInfo() string {
   834  	buffer := bytes.NewBufferString("")
   835  	fmt.Fprintf(buffer, "conds:%s",
   836  		memex.SortedExplainExpressionList(p.conditions))
   837  	fmt.Fprintf(buffer, ", handle:%s", p.handleDefCauss)
   838  	return buffer.String()
   839  }
   840  
   841  func explainByItems(buffer *bytes.Buffer, byItems []*soliton.ByItems) *bytes.Buffer {
   842  	for i, item := range byItems {
   843  		if item.Desc {
   844  			fmt.Fprintf(buffer, "%s:desc", item.Expr.ExplainInfo())
   845  		} else {
   846  			fmt.Fprintf(buffer, "%s", item.Expr.ExplainInfo())
   847  		}
   848  
   849  		if i+1 < len(byItems) {
   850  			buffer.WriteString(", ")
   851  		}
   852  	}
   853  	return buffer
   854  }
   855  
   856  func explainNormalizedByItems(buffer *bytes.Buffer, byItems []*soliton.ByItems) *bytes.Buffer {
   857  	for i, item := range byItems {
   858  		if item.Desc {
   859  			fmt.Fprintf(buffer, "%s:desc", item.Expr.ExplainNormalizedInfo())
   860  		} else {
   861  			fmt.Fprintf(buffer, "%s", item.Expr.ExplainNormalizedInfo())
   862  		}
   863  
   864  		if i+1 < len(byItems) {
   865  			buffer.WriteString(", ")
   866  		}
   867  	}
   868  	return buffer
   869  }
   870  
   871  // ExplainInfo implements Causet interface.
   872  func (p *LogicalSort) ExplainInfo() string {
   873  	buffer := bytes.NewBufferString("")
   874  	return explainByItems(buffer, p.ByItems).String()
   875  }
   876  
   877  // ExplainInfo implements Causet interface.
   878  func (p *LogicalTopN) ExplainInfo() string {
   879  	buffer := bytes.NewBufferString("")
   880  	buffer = explainByItems(buffer, p.ByItems)
   881  	fmt.Fprintf(buffer, ", offset:%v, count:%v", p.Offset, p.Count)
   882  	return buffer.String()
   883  }
   884  
   885  // ExplainInfo implements Causet interface.
   886  func (p *LogicalLimit) ExplainInfo() string {
   887  	return fmt.Sprintf("offset:%v, count:%v", p.Offset, p.Count)
   888  }
   889  
   890  // ExplainInfo implements Causet interface.
   891  func (p *LogicalBlockScan) ExplainInfo() string {
   892  	buffer := bytes.NewBufferString(p.Source.ExplainInfo())
   893  	if p.Source.handleDefCauss != nil {
   894  		fmt.Fprintf(buffer, ", pk col:%s", p.Source.handleDefCauss)
   895  	}
   896  	if len(p.AccessConds) > 0 {
   897  		fmt.Fprintf(buffer, ", cond:%v", p.AccessConds)
   898  	}
   899  	return buffer.String()
   900  }
   901  
   902  // ExplainInfo implements Causet interface.
   903  func (p *LogicalIndexScan) ExplainInfo() string {
   904  	buffer := bytes.NewBufferString(p.Source.ExplainInfo())
   905  	index := p.Index
   906  	if len(index.DeferredCausets) > 0 {
   907  		buffer.WriteString(", index:")
   908  		for i, idxDefCaus := range index.DeferredCausets {
   909  			buffer.WriteString(idxDefCaus.Name.O)
   910  			if i+1 < len(index.DeferredCausets) {
   911  				buffer.WriteString(", ")
   912  			}
   913  		}
   914  	}
   915  	if len(p.AccessConds) > 0 {
   916  		fmt.Fprintf(buffer, ", cond:%v", p.AccessConds)
   917  	}
   918  	return buffer.String()
   919  }
   920  
   921  // ExplainInfo implements Causet interface.
   922  func (p *EinsteinDBSingleGather) ExplainInfo() string {
   923  	buffer := bytes.NewBufferString(p.Source.ExplainInfo())
   924  	if p.IsIndexGather {
   925  		buffer.WriteString(", index:" + p.Index.Name.String())
   926  	}
   927  	return buffer.String()
   928  }
   929  
   930  // MetricBlockTimeFormat is the time format for metric causet explain and format.
   931  const MetricBlockTimeFormat = "2006-01-02 15:04:05.999"
   932  
   933  // ExplainInfo implements Causet interface.
   934  func (p *PhysicalMemBlock) ExplainInfo() string {
   935  	accessObject, operatorInfo := p.AccessObject(), p.OperatorInfo(false)
   936  	if len(operatorInfo) == 0 {
   937  		return accessObject
   938  	}
   939  	return accessObject + ", " + operatorInfo
   940  }
   941  
   942  // AccessObject implements dataAccesser interface.
   943  func (p *PhysicalMemBlock) AccessObject() string {
   944  	return "causet:" + p.Block.Name.O
   945  }
   946  
   947  // OperatorInfo implements dataAccesser interface.
   948  func (p *PhysicalMemBlock) OperatorInfo(_ bool) string {
   949  	if p.Extractor != nil {
   950  		return p.Extractor.explainInfo(p)
   951  	}
   952  	return ""
   953  }