github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/petri/acyclic/causet/embedded/common_plans.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  	"context"
    19  	"fmt"
    20  	"strconv"
    21  	"strings"
    22  
    23  	"github.com/whtcorpsinc/BerolinaSQL/allegrosql"
    24  	"github.com/whtcorpsinc/BerolinaSQL/ast"
    25  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    26  	"github.com/whtcorpsinc/errors"
    27  	"github.com/whtcorpsinc/milevadb/causet"
    28  	"github.com/whtcorpsinc/milevadb/causet/blocks"
    29  	"github.com/whtcorpsinc/milevadb/ekv"
    30  	"github.com/whtcorpsinc/milevadb/memex"
    31  	"github.com/whtcorpsinc/milevadb/metrics"
    32  	"github.com/whtcorpsinc/milevadb/privilege"
    33  	"github.com/whtcorpsinc/milevadb/schemareplicant"
    34  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    35  	"github.com/whtcorpsinc/milevadb/soliton/ekvcache"
    36  	"github.com/whtcorpsinc/milevadb/soliton/hint"
    37  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    38  	"github.com/whtcorpsinc/milevadb/soliton/ranger"
    39  	"github.com/whtcorpsinc/milevadb/soliton/texttree"
    40  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    41  	"github.com/whtcorpsinc/milevadb/stochastikctx/variable"
    42  	"github.com/whtcorpsinc/milevadb/types"
    43  	driver "github.com/whtcorpsinc/milevadb/types/BerolinaSQL_driver"
    44  	"go.uber.org/zap"
    45  )
    46  
    47  var planCacheCounter = metrics.CausetCacheCounter.WithLabelValues("prepare")
    48  
    49  // ShowDBS is for showing DBS information.
    50  type ShowDBS struct {
    51  	baseSchemaProducer
    52  }
    53  
    54  // ShowSlow is for showing slow queries.
    55  type ShowSlow struct {
    56  	baseSchemaProducer
    57  
    58  	*ast.ShowSlow
    59  }
    60  
    61  // ShowDBSJobQueries is for showing DBS job queries allegrosql.
    62  type ShowDBSJobQueries struct {
    63  	baseSchemaProducer
    64  
    65  	JobIDs []int64
    66  }
    67  
    68  // ShowNextRowID is for showing the next global event ID.
    69  type ShowNextRowID struct {
    70  	baseSchemaProducer
    71  	BlockName *ast.BlockName
    72  }
    73  
    74  // CheckBlock is used for checking causet data, built from the 'admin check causet' memex.
    75  type CheckBlock struct {
    76  	baseSchemaProducer
    77  
    78  	DBName             string
    79  	Block              causet.Block
    80  	IndexInfos         []*perceptron.IndexInfo
    81  	IndexLookUpReaders []*PhysicalIndexLookUpReader
    82  	ChecHoTTex         bool
    83  }
    84  
    85  // RecoverIndex is used for backfilling corrupted index data.
    86  type RecoverIndex struct {
    87  	baseSchemaProducer
    88  
    89  	Block     *ast.BlockName
    90  	IndexName string
    91  }
    92  
    93  // CleanupIndex is used to delete dangling index data.
    94  type CleanupIndex struct {
    95  	baseSchemaProducer
    96  
    97  	Block     *ast.BlockName
    98  	IndexName string
    99  }
   100  
   101  // ChecHoTTexRange is used for checking index data, output the index values that handle within begin and end.
   102  type ChecHoTTexRange struct {
   103  	baseSchemaProducer
   104  
   105  	Block     *ast.BlockName
   106  	IndexName string
   107  
   108  	HandleRanges []ast.HandleRange
   109  }
   110  
   111  // ChecksumBlock is used for calculating causet checksum, built from the `admin checksum causet` memex.
   112  type ChecksumBlock struct {
   113  	baseSchemaProducer
   114  
   115  	Blocks []*ast.BlockName
   116  }
   117  
   118  // CancelDBSJobs represents a cancel DBS jobs plan.
   119  type CancelDBSJobs struct {
   120  	baseSchemaProducer
   121  
   122  	JobIDs []int64
   123  }
   124  
   125  // ReloadExprPushdownBlacklist reloads the data from expr_pushdown_blacklist causet.
   126  type ReloadExprPushdownBlacklist struct {
   127  	baseSchemaProducer
   128  }
   129  
   130  // ReloadOptMemruleBlacklist reloads the data from opt_rule_blacklist causet.
   131  type ReloadOptMemruleBlacklist struct {
   132  	baseSchemaProducer
   133  }
   134  
   135  // AdminPluginsCausetAction indicate action will be taken on plugins.
   136  type AdminPluginsCausetAction int
   137  
   138  const (
   139  	// Enable indicates enable plugins.
   140  	Enable AdminPluginsCausetAction = iota + 1
   141  	// Disable indicates disable plugins.
   142  	Disable
   143  )
   144  
   145  // AdminPlugins administrates milevadb plugins.
   146  type AdminPlugins struct {
   147  	baseSchemaProducer
   148  	CausetAction AdminPluginsCausetAction
   149  	Plugins      []string
   150  }
   151  
   152  // AdminShowTelemetry displays telemetry status including tracking ID, status and so on.
   153  type AdminShowTelemetry struct {
   154  	baseSchemaProducer
   155  }
   156  
   157  // AdminResetTelemetryID regenerates a new telemetry tracking ID.
   158  type AdminResetTelemetryID struct {
   159  	baseSchemaProducer
   160  }
   161  
   162  // Change represents a change plan.
   163  type Change struct {
   164  	baseSchemaProducer
   165  	*ast.ChangeStmt
   166  }
   167  
   168  // Prepare represents prepare plan.
   169  type Prepare struct {
   170  	baseSchemaProducer
   171  
   172  	Name           string
   173  	ALLEGROSQLText string
   174  }
   175  
   176  // InterDircute represents prepare plan.
   177  type InterDircute struct {
   178  	baseSchemaProducer
   179  
   180  	Name          string
   181  	UsingVars     []memex.Expression
   182  	PrepareParams []types.Causet
   183  	InterDircID   uint32
   184  	Stmt          ast.StmtNode
   185  	StmtType      string
   186  	Causet        Causet
   187  }
   188  
   189  // OptimizePreparedCauset optimizes the prepared memex.
   190  func (e *InterDircute) OptimizePreparedCauset(ctx context.Context, sctx stochastikctx.Context, is schemareplicant.SchemaReplicant) error {
   191  	vars := sctx.GetStochastikVars()
   192  	if e.Name != "" {
   193  		e.InterDircID = vars.PreparedStmtNameToID[e.Name]
   194  	}
   195  	preparedPointer, ok := vars.PreparedStmts[e.InterDircID]
   196  	if !ok {
   197  		return errors.Trace(ErrStmtNotFound)
   198  	}
   199  	preparedObj, ok := preparedPointer.(*CachedPrepareStmt)
   200  	if !ok {
   201  		return errors.Errorf("invalid CachedPrepareStmt type")
   202  	}
   203  	prepared := preparedObj.PreparedAst
   204  	vars.StmtCtx.StmtType = prepared.StmtType
   205  
   206  	paramLen := len(e.PrepareParams)
   207  	if paramLen > 0 {
   208  		// for binary protocol execute, argument is placed in vars.PrepareParams
   209  		if len(prepared.Params) != paramLen {
   210  			return errors.Trace(ErrWrongParamCount)
   211  		}
   212  		vars.PreparedParams = e.PrepareParams
   213  		for i, val := range vars.PreparedParams {
   214  			param := prepared.Params[i].(*driver.ParamMarkerExpr)
   215  			param.Causet = val
   216  			param.InInterDircute = true
   217  		}
   218  	} else {
   219  		// for `execute stmt using @a, @b, @c`, using value in e.UsingVars
   220  		if len(prepared.Params) != len(e.UsingVars) {
   221  			return errors.Trace(ErrWrongParamCount)
   222  		}
   223  
   224  		for i, usingVar := range e.UsingVars {
   225  			val, err := usingVar.Eval(chunk.Row{})
   226  			if err != nil {
   227  				return err
   228  			}
   229  			param := prepared.Params[i].(*driver.ParamMarkerExpr)
   230  			param.Causet = val
   231  			param.InInterDircute = true
   232  			vars.PreparedParams = append(vars.PreparedParams, val)
   233  		}
   234  	}
   235  
   236  	if prepared.SchemaVersion != is.SchemaMetaVersion() {
   237  		// In order to avoid some correctness issues, we have to clear the
   238  		// cached plan once the schemaReplicant version is changed.
   239  		// Cached plan in prepared struct does NOT have a "cache key" with
   240  		// schemaReplicant version like prepared plan cache key
   241  		prepared.CachedCauset = nil
   242  		preparedObj.InterlockingDirectorate = nil
   243  		// If the schemaReplicant version has changed we need to preprocess it again,
   244  		// if this time it failed, the real reason for the error is schemaReplicant changed.
   245  		err := Preprocess(sctx, prepared.Stmt, is, InPrepare)
   246  		if err != nil {
   247  			return ErrSchemaChanged.GenWithStack("Schema change caused error: %s", err.Error())
   248  		}
   249  		prepared.SchemaVersion = is.SchemaMetaVersion()
   250  	}
   251  	err := e.getPhysicalCauset(ctx, sctx, is, preparedObj)
   252  	if err != nil {
   253  		return err
   254  	}
   255  	e.Stmt = prepared.Stmt
   256  	return nil
   257  }
   258  
   259  func (e *InterDircute) checkPreparedPriv(ctx context.Context, sctx stochastikctx.Context,
   260  	preparedObj *CachedPrepareStmt, is schemareplicant.SchemaReplicant) error {
   261  	if pm := privilege.GetPrivilegeManager(sctx); pm != nil {
   262  		if err := CheckPrivilege(sctx.GetStochastikVars().ActiveRoles, pm, preparedObj.VisitInfos); err != nil {
   263  			return err
   264  		}
   265  	}
   266  	err := CheckBlockLock(sctx, is, preparedObj.VisitInfos)
   267  	return err
   268  }
   269  
   270  func (e *InterDircute) setFoundInCausetCache(sctx stochastikctx.Context, opt bool) error {
   271  	vars := sctx.GetStochastikVars()
   272  	err := vars.SetSystemVar(variable.MilevaDBFoundInCausetCache, variable.BoolToIntStr(opt))
   273  	return err
   274  }
   275  
   276  func (e *InterDircute) getPhysicalCauset(ctx context.Context, sctx stochastikctx.Context, is schemareplicant.SchemaReplicant, preparedStmt *CachedPrepareStmt) error {
   277  	stmtCtx := sctx.GetStochastikVars().StmtCtx
   278  	prepared := preparedStmt.PreparedAst
   279  	stmtCtx.UseCache = prepared.UseCache
   280  	var cacheKey ekvcache.Key
   281  	if prepared.UseCache {
   282  		cacheKey = NewPSTMTCausetCacheKey(sctx.GetStochastikVars(), e.InterDircID, prepared.SchemaVersion)
   283  	}
   284  	if prepared.CachedCauset != nil {
   285  		// Rewriting the memex in the select.where condition  will convert its
   286  		// type from "paramMarker" to "Constant".When Point Select queries are executed,
   287  		// the memex in the where condition will not be evaluated,
   288  		// so you don't need to consider whether prepared.useCache is enabled.
   289  		plan := prepared.CachedCauset.(Causet)
   290  		names := prepared.CachedNames.(types.NameSlice)
   291  		err := e.rebuildRange(plan)
   292  		if err != nil {
   293  			logutil.BgLogger().Debug("rebuild range failed", zap.Error(err))
   294  			goto REBUILD
   295  		}
   296  		if metrics.ResetblockCausetCacheCounterFortTest {
   297  			metrics.CausetCacheCounter.WithLabelValues("prepare").Inc()
   298  		} else {
   299  			planCacheCounter.Inc()
   300  		}
   301  		err = e.setFoundInCausetCache(sctx, true)
   302  		if err != nil {
   303  			return err
   304  		}
   305  		e.names = names
   306  		e.Causet = plan
   307  		stmtCtx.PointInterDirc = true
   308  		return nil
   309  	}
   310  	if prepared.UseCache {
   311  		if cacheValue, exists := sctx.PreparedCausetCache().Get(cacheKey); exists {
   312  			if err := e.checkPreparedPriv(ctx, sctx, preparedStmt, is); err != nil {
   313  				return err
   314  			}
   315  			cachedVal := cacheValue.(*PSTMTCausetCacheValue)
   316  			planValid := true
   317  			for tblInfo, unionScan := range cachedVal.TblInfo2UnionScan {
   318  				if !unionScan && blockHasDirtyContent(sctx, tblInfo) {
   319  					planValid = false
   320  					// TODO we can inject UnionScan into cached plan to avoid invalidating it, though
   321  					// rebuilding the filters in UnionScan is pretty trivial.
   322  					sctx.PreparedCausetCache().Delete(cacheKey)
   323  					break
   324  				}
   325  			}
   326  			if planValid {
   327  				err := e.rebuildRange(cachedVal.Causet)
   328  				if err != nil {
   329  					logutil.BgLogger().Debug("rebuild range failed", zap.Error(err))
   330  					goto REBUILD
   331  				}
   332  				err = e.setFoundInCausetCache(sctx, true)
   333  				if err != nil {
   334  					return err
   335  				}
   336  				if metrics.ResetblockCausetCacheCounterFortTest {
   337  					metrics.CausetCacheCounter.WithLabelValues("prepare").Inc()
   338  				} else {
   339  					planCacheCounter.Inc()
   340  				}
   341  				e.names = cachedVal.OutPutNames
   342  				e.Causet = cachedVal.Causet
   343  				stmtCtx.SetCausetDigest(preparedStmt.NormalizedCauset, preparedStmt.CausetDigest)
   344  				return nil
   345  			}
   346  		}
   347  	}
   348  
   349  REBUILD:
   350  	stmt := TryAddExtraLimit(sctx, prepared.Stmt)
   351  	p, names, err := OptimizeAstNode(ctx, sctx, stmt, is)
   352  	if err != nil {
   353  		return err
   354  	}
   355  	err = e.tryCachePointCauset(ctx, sctx, preparedStmt, is, p)
   356  	if err != nil {
   357  		return err
   358  	}
   359  	e.names = names
   360  	e.Causet = p
   361  	_, isBlockDual := p.(*PhysicalBlockDual)
   362  	if !isBlockDual && prepared.UseCache {
   363  		cached := NewPSTMTCausetCacheValue(p, names, stmtCtx.TblInfo2UnionScan)
   364  		preparedStmt.NormalizedCauset, preparedStmt.CausetDigest = NormalizeCauset(p)
   365  		stmtCtx.SetCausetDigest(preparedStmt.NormalizedCauset, preparedStmt.CausetDigest)
   366  		sctx.PreparedCausetCache().Put(cacheKey, cached)
   367  	}
   368  	err = e.setFoundInCausetCache(sctx, false)
   369  	return err
   370  }
   371  
   372  // tryCachePointCauset will try to cache point execution plan, there may be some
   373  // short paths for these executions, currently "point select" and "point uFIDelate"
   374  func (e *InterDircute) tryCachePointCauset(ctx context.Context, sctx stochastikctx.Context,
   375  	preparedStmt *CachedPrepareStmt, is schemareplicant.SchemaReplicant, p Causet) error {
   376  	var (
   377  		prepared = preparedStmt.PreparedAst
   378  		ok       bool
   379  		err      error
   380  		names    types.NameSlice
   381  	)
   382  	switch p.(type) {
   383  	case *PointGetCauset:
   384  		ok, err = IsPointGetWithPKOrUniqueKeyByAutoCommit(sctx, p)
   385  		names = p.OutputNames()
   386  		if err != nil {
   387  			return err
   388  		}
   389  	case *UFIDelate:
   390  		ok, err = IsPointUFIDelateByAutoCommit(sctx, p)
   391  		if err != nil {
   392  			return err
   393  		}
   394  		if ok {
   395  			// make constant memex causetstore paramMarker
   396  			sctx.GetStochastikVars().StmtCtx.PointInterDirc = true
   397  			p, names, err = OptimizeAstNode(ctx, sctx, prepared.Stmt, is)
   398  		}
   399  	}
   400  	if ok {
   401  		// just cache point plan now
   402  		prepared.CachedCauset = p
   403  		prepared.CachedNames = names
   404  		preparedStmt.NormalizedCauset, preparedStmt.CausetDigest = NormalizeCauset(p)
   405  		sctx.GetStochastikVars().StmtCtx.SetCausetDigest(preparedStmt.NormalizedCauset, preparedStmt.CausetDigest)
   406  	}
   407  	return err
   408  }
   409  
   410  func (e *InterDircute) rebuildRange(p Causet) error {
   411  	sctx := p.SCtx()
   412  	sc := p.SCtx().GetStochastikVars().StmtCtx
   413  	var err error
   414  	switch x := p.(type) {
   415  	case *PhysicalBlockReader:
   416  		ts := x.BlockCausets[0].(*PhysicalBlockScan)
   417  		var pkDefCaus *memex.DeferredCauset
   418  		if ts.Block.IsCommonHandle {
   419  			pk := blocks.FindPrimaryIndex(ts.Block)
   420  			pkDefCauss := make([]*memex.DeferredCauset, 0, len(pk.DeferredCausets))
   421  			pkDefCaussLen := make([]int, 0, len(pk.DeferredCausets))
   422  			for _, colInfo := range pk.DeferredCausets {
   423  				pkDefCauss = append(pkDefCauss, memex.DefCausInfo2DefCaus(ts.schemaReplicant.DeferredCausets, ts.Block.DeferredCausets[colInfo.Offset]))
   424  				pkDefCaussLen = append(pkDefCaussLen, colInfo.Length)
   425  			}
   426  			res, err := ranger.DetachCondAndBuildRangeForIndex(p.SCtx(), ts.AccessCondition, pkDefCauss, pkDefCaussLen)
   427  			if err != nil {
   428  				return err
   429  			}
   430  			ts.Ranges = res.Ranges
   431  		} else {
   432  			if ts.Block.PKIsHandle {
   433  				if pkDefCausInfo := ts.Block.GetPkDefCausInfo(); pkDefCausInfo != nil {
   434  					pkDefCaus = memex.DefCausInfo2DefCaus(ts.schemaReplicant.DeferredCausets, pkDefCausInfo)
   435  				}
   436  			}
   437  			if pkDefCaus != nil {
   438  				ts.Ranges, err = ranger.BuildBlockRange(ts.AccessCondition, sc, pkDefCaus.RetType)
   439  				if err != nil {
   440  					return err
   441  				}
   442  			} else {
   443  				ts.Ranges = ranger.FullIntRange(false)
   444  			}
   445  		}
   446  	case *PhysicalIndexReader:
   447  		is := x.IndexCausets[0].(*PhysicalIndexScan)
   448  		is.Ranges, err = e.buildRangeForIndexScan(sctx, is)
   449  		if err != nil {
   450  			return err
   451  		}
   452  	case *PhysicalIndexLookUpReader:
   453  		is := x.IndexCausets[0].(*PhysicalIndexScan)
   454  		is.Ranges, err = e.buildRangeForIndexScan(sctx, is)
   455  		if err != nil {
   456  			return err
   457  		}
   458  	case *PointGetCauset:
   459  		// if access condition is not nil, which means it's a point get generated by cbo.
   460  		if x.AccessConditions != nil {
   461  			if x.IndexInfo != nil {
   462  				ranges, err := ranger.DetachCondAndBuildRangeForIndex(x.ctx, x.AccessConditions, x.IdxDefCauss, x.IdxDefCausLens)
   463  				if err != nil {
   464  					return err
   465  				}
   466  				for i := range x.IndexValues {
   467  					x.IndexValues[i] = ranges.Ranges[0].LowVal[i]
   468  				}
   469  			} else {
   470  				var pkDefCaus *memex.DeferredCauset
   471  				if x.TblInfo.PKIsHandle {
   472  					if pkDefCausInfo := x.TblInfo.GetPkDefCausInfo(); pkDefCausInfo != nil {
   473  						pkDefCaus = memex.DefCausInfo2DefCaus(x.schemaReplicant.DeferredCausets, pkDefCausInfo)
   474  					}
   475  				}
   476  				if pkDefCaus != nil {
   477  					ranges, err := ranger.BuildBlockRange(x.AccessConditions, x.ctx.GetStochastikVars().StmtCtx, pkDefCaus.RetType)
   478  					if err != nil {
   479  						return err
   480  					}
   481  					x.Handle = ekv.IntHandle(ranges[0].LowVal[0].GetInt64())
   482  				}
   483  			}
   484  		}
   485  		// The code should never run here as long as we're not using point get for partition causet.
   486  		// And if we change the logic one day, here work as defensive programming to cache the error.
   487  		if x.PartitionInfo != nil {
   488  			return errors.New("point get for partition causet can not use plan cache")
   489  		}
   490  		if x.HandleParam != nil {
   491  			var iv int64
   492  			iv, err = x.HandleParam.Causet.ToInt64(sc)
   493  			if err != nil {
   494  				return err
   495  			}
   496  			x.Handle = ekv.IntHandle(iv)
   497  			return nil
   498  		}
   499  		for i, param := range x.IndexValueParams {
   500  			if param != nil {
   501  				x.IndexValues[i] = param.Causet
   502  			}
   503  		}
   504  		return nil
   505  	case *BatchPointGetCauset:
   506  		// if access condition is not nil, which means it's a point get generated by cbo.
   507  		if x.AccessConditions != nil {
   508  			if x.IndexInfo != nil {
   509  				ranges, err := ranger.DetachCondAndBuildRangeForIndex(x.ctx, x.AccessConditions, x.IdxDefCauss, x.IdxDefCausLens)
   510  				if err != nil {
   511  					return err
   512  				}
   513  				for i := range x.IndexValues {
   514  					for j := range ranges.Ranges[i].LowVal {
   515  						x.IndexValues[i][j] = ranges.Ranges[i].LowVal[j]
   516  					}
   517  				}
   518  			} else {
   519  				var pkDefCaus *memex.DeferredCauset
   520  				if x.TblInfo.PKIsHandle {
   521  					if pkDefCausInfo := x.TblInfo.GetPkDefCausInfo(); pkDefCausInfo != nil {
   522  						pkDefCaus = memex.DefCausInfo2DefCaus(x.schemaReplicant.DeferredCausets, pkDefCausInfo)
   523  					}
   524  				}
   525  				if pkDefCaus != nil {
   526  					ranges, err := ranger.BuildBlockRange(x.AccessConditions, x.ctx.GetStochastikVars().StmtCtx, pkDefCaus.RetType)
   527  					if err != nil {
   528  						return err
   529  					}
   530  					for i := range ranges {
   531  						x.Handles[i] = ekv.IntHandle(ranges[i].LowVal[0].GetInt64())
   532  					}
   533  				}
   534  			}
   535  		}
   536  		for i, param := range x.HandleParams {
   537  			if param != nil {
   538  				var iv int64
   539  				iv, err = param.Causet.ToInt64(sc)
   540  				if err != nil {
   541  					return err
   542  				}
   543  				x.Handles[i] = ekv.IntHandle(iv)
   544  			}
   545  		}
   546  		for i, params := range x.IndexValueParams {
   547  			if len(params) < 1 {
   548  				continue
   549  			}
   550  			for j, param := range params {
   551  				if param != nil {
   552  					x.IndexValues[i][j] = param.Causet
   553  				}
   554  			}
   555  		}
   556  	case PhysicalCauset:
   557  		for _, child := range x.Children() {
   558  			err = e.rebuildRange(child)
   559  			if err != nil {
   560  				return err
   561  			}
   562  		}
   563  	case *Insert:
   564  		if x.SelectCauset != nil {
   565  			return e.rebuildRange(x.SelectCauset)
   566  		}
   567  	case *UFIDelate:
   568  		if x.SelectCauset != nil {
   569  			return e.rebuildRange(x.SelectCauset)
   570  		}
   571  	case *Delete:
   572  		if x.SelectCauset != nil {
   573  			return e.rebuildRange(x.SelectCauset)
   574  		}
   575  	}
   576  	return nil
   577  }
   578  
   579  func (e *InterDircute) buildRangeForIndexScan(sctx stochastikctx.Context, is *PhysicalIndexScan) ([]*ranger.Range, error) {
   580  	if len(is.IdxDefCauss) == 0 {
   581  		return ranger.FullRange(), nil
   582  	}
   583  	res, err := ranger.DetachCondAndBuildRangeForIndex(sctx, is.AccessCondition, is.IdxDefCauss, is.IdxDefCausLens)
   584  	if err != nil {
   585  		return nil, err
   586  	}
   587  	return res.Ranges, nil
   588  }
   589  
   590  // Deallocate represents deallocate plan.
   591  type Deallocate struct {
   592  	baseSchemaProducer
   593  
   594  	Name string
   595  }
   596  
   597  // Set represents a plan for set stmt.
   598  type Set struct {
   599  	baseSchemaProducer
   600  
   601  	VarAssigns []*memex.VarAssignment
   602  }
   603  
   604  // SetConfig represents a plan for set config stmt.
   605  type SetConfig struct {
   606  	baseSchemaProducer
   607  
   608  	Type     string
   609  	Instance string
   610  	Name     string
   611  	Value    memex.Expression
   612  }
   613  
   614  // ALLEGROSQLBindOpType repreents the ALLEGROALLEGROSQL bind type
   615  type ALLEGROSQLBindOpType int
   616  
   617  const (
   618  	// OpALLEGROSQLBindCreate represents the operation to create a ALLEGROALLEGROSQL bind.
   619  	OpALLEGROSQLBindCreate ALLEGROSQLBindOpType = iota
   620  	// OpALLEGROSQLBindDrop represents the operation to drop a ALLEGROALLEGROSQL bind.
   621  	OpALLEGROSQLBindDrop
   622  	// OpFlushBindings is used to flush plan bindings.
   623  	OpFlushBindings
   624  	// OpCaptureBindings is used to capture plan bindings.
   625  	OpCaptureBindings
   626  	// OpEvolveBindings is used to evolve plan binding.
   627  	OpEvolveBindings
   628  	// OpReloadBindings is used to reload plan binding.
   629  	OpReloadBindings
   630  )
   631  
   632  // ALLEGROSQLBindCauset represents a plan for ALLEGROALLEGROSQL bind.
   633  type ALLEGROSQLBindCauset struct {
   634  	baseSchemaProducer
   635  
   636  	ALLEGROSQLBindOp    ALLEGROSQLBindOpType
   637  	NormdOrigALLEGROSQL string
   638  	BindALLEGROSQL      string
   639  	IsGlobal            bool
   640  	BindStmt            ast.StmtNode
   641  	EDB                 string
   642  	Charset             string
   643  	DefCauslation       string
   644  }
   645  
   646  // Simple represents a simple memex plan which doesn't need any optimization.
   647  type Simple struct {
   648  	baseSchemaProducer
   649  
   650  	Statement ast.StmtNode
   651  }
   652  
   653  // InsertGeneratedDeferredCausets is for completing generated columns in Insert.
   654  // We resolve generation memexs in plan, and eval those in interlock.
   655  type InsertGeneratedDeferredCausets struct {
   656  	DeferredCausets []*ast.DeferredCausetName
   657  	Exprs           []memex.Expression
   658  	OnDuplicates    []*memex.Assignment
   659  }
   660  
   661  // Insert represents an insert plan.
   662  type Insert struct {
   663  	baseSchemaProducer
   664  
   665  	Block             causet.Block
   666  	blockSchema       *memex.Schema
   667  	blockDefCausNames types.NameSlice
   668  	DeferredCausets   []*ast.DeferredCausetName
   669  	Lists             [][]memex.Expression
   670  	SetList           []*memex.Assignment
   671  
   672  	OnDuplicate        []*memex.Assignment
   673  	Schema4OnDuplicate *memex.Schema
   674  	names4OnDuplicate  types.NameSlice
   675  
   676  	GenDefCauss InsertGeneratedDeferredCausets
   677  
   678  	SelectCauset PhysicalCauset
   679  
   680  	IsReplace bool
   681  
   682  	// NeedFillDefaultValue is true when expr in value list reference other column.
   683  	NeedFillDefaultValue bool
   684  
   685  	AllAssignmentsAreConstant bool
   686  }
   687  
   688  // UFIDelate represents UFIDelate plan.
   689  type UFIDelate struct {
   690  	baseSchemaProducer
   691  
   692  	OrderedList []*memex.Assignment
   693  
   694  	AllAssignmentsAreConstant bool
   695  
   696  	SelectCauset PhysicalCauset
   697  
   698  	TblDefCausPosInfos TblDefCausPosInfoSlice
   699  
   700  	// Used when partition sets are given.
   701  	// e.g. uFIDelate t partition(p0) set a = 1;
   702  	PartitionedBlock []causet.PartitionedBlock
   703  }
   704  
   705  // Delete represents a delete plan.
   706  type Delete struct {
   707  	baseSchemaProducer
   708  
   709  	IsMultiBlock bool
   710  
   711  	SelectCauset PhysicalCauset
   712  
   713  	TblDefCausPosInfos TblDefCausPosInfoSlice
   714  }
   715  
   716  // AnalyzeBlockID is hybrid causet id used to analyze causet.
   717  type AnalyzeBlockID struct {
   718  	PersistID      int64
   719  	DefCauslectIDs []int64
   720  }
   721  
   722  // StoreAsDefCauslectID indicates whether collect causet id is same as persist causet id.
   723  // for new partition implementation is TRUE but FALSE for old partition implementation
   724  func (h *AnalyzeBlockID) StoreAsDefCauslectID() bool {
   725  	return h.PersistID == h.DefCauslectIDs[0]
   726  }
   727  
   728  func (h *AnalyzeBlockID) String() string {
   729  	return fmt.Sprintf("%d => %v", h.DefCauslectIDs, h.PersistID)
   730  }
   731  
   732  // Equals indicates whether two causet id is equal.
   733  func (h *AnalyzeBlockID) Equals(t *AnalyzeBlockID) bool {
   734  	if h == t {
   735  		return true
   736  	}
   737  	if h == nil || t == nil {
   738  		return false
   739  	}
   740  	if h.PersistID != t.PersistID {
   741  		return false
   742  	}
   743  	if len(h.DefCauslectIDs) != len(t.DefCauslectIDs) {
   744  		return false
   745  	}
   746  	if len(h.DefCauslectIDs) == 1 {
   747  		return h.DefCauslectIDs[0] == t.DefCauslectIDs[0]
   748  	}
   749  	for _, hp := range h.DefCauslectIDs {
   750  		var matchOne bool
   751  		for _, tp := range t.DefCauslectIDs {
   752  			if tp == hp {
   753  				matchOne = true
   754  				break
   755  			}
   756  		}
   757  		if !matchOne {
   758  			return false
   759  		}
   760  	}
   761  	return true
   762  }
   763  
   764  // analyzeInfo is used to causetstore the database name, causet name and partition name of analyze task.
   765  type analyzeInfo struct {
   766  	DBName        string
   767  	BlockName     string
   768  	PartitionName string
   769  	BlockID       AnalyzeBlockID
   770  	Incremental   bool
   771  }
   772  
   773  // AnalyzeDeferredCausetsTask is used for analyze columns.
   774  type AnalyzeDeferredCausetsTask struct {
   775  	HandleDefCauss HandleDefCauss
   776  	DefCaussInfo   []*perceptron.DeferredCausetInfo
   777  	TblInfo        *perceptron.BlockInfo
   778  	analyzeInfo
   779  }
   780  
   781  // AnalyzeIndexTask is used for analyze index.
   782  type AnalyzeIndexTask struct {
   783  	IndexInfo *perceptron.IndexInfo
   784  	TblInfo   *perceptron.BlockInfo
   785  	analyzeInfo
   786  }
   787  
   788  // Analyze represents an analyze plan
   789  type Analyze struct {
   790  	baseSchemaProducer
   791  
   792  	DefCausTasks []AnalyzeDeferredCausetsTask
   793  	IdxTasks     []AnalyzeIndexTask
   794  	Opts         map[ast.AnalyzeOptionType]uint64
   795  }
   796  
   797  // LoadData represents a loaddata plan.
   798  type LoadData struct {
   799  	baseSchemaProducer
   800  
   801  	IsLocal         bool
   802  	OnDuplicate     ast.OnDuplicateKeyHandlingType
   803  	Path            string
   804  	Block           *ast.BlockName
   805  	DeferredCausets []*ast.DeferredCausetName
   806  	FieldsInfo      *ast.FieldsClause
   807  	LinesInfo       *ast.LinesClause
   808  	IgnoreLines     uint64
   809  
   810  	DeferredCausetAssignments  []*ast.Assignment
   811  	DeferredCausetsAndUserVars []*ast.DeferredCausetNameOrUserVar
   812  
   813  	GenDefCauss InsertGeneratedDeferredCausets
   814  }
   815  
   816  // LoadStats represents a load stats plan.
   817  type LoadStats struct {
   818  	baseSchemaProducer
   819  
   820  	Path string
   821  }
   822  
   823  // IndexAdvise represents a index advise plan.
   824  type IndexAdvise struct {
   825  	baseSchemaProducer
   826  
   827  	IsLocal     bool
   828  	Path        string
   829  	MaxMinutes  uint64
   830  	MaxIndexNum *ast.MaxIndexNumClause
   831  	LinesInfo   *ast.LinesClause
   832  }
   833  
   834  // SplitRegion represents a split regions plan.
   835  type SplitRegion struct {
   836  	baseSchemaProducer
   837  
   838  	BlockInfo      *perceptron.BlockInfo
   839  	PartitionNames []perceptron.CIStr
   840  	IndexInfo      *perceptron.IndexInfo
   841  	Lower          []types.Causet
   842  	Upper          []types.Causet
   843  	Num            int
   844  	ValueLists     [][]types.Causet
   845  }
   846  
   847  // SplitRegionStatus represents a split regions status plan.
   848  type SplitRegionStatus struct {
   849  	baseSchemaProducer
   850  
   851  	Block     causet.Block
   852  	IndexInfo *perceptron.IndexInfo
   853  }
   854  
   855  // DBS represents a DBS memex plan.
   856  type DBS struct {
   857  	baseSchemaProducer
   858  
   859  	Statement ast.DBSNode
   860  }
   861  
   862  // SelectInto represents a select-into plan.
   863  type SelectInto struct {
   864  	baseSchemaProducer
   865  
   866  	TargetCauset Causet
   867  	IntoOpt      *ast.SelectIntoOption
   868  }
   869  
   870  // Explain represents a explain plan.
   871  type Explain struct {
   872  	baseSchemaProducer
   873  
   874  	TargetCauset  Causet
   875  	Format        string
   876  	Analyze       bool
   877  	InterDircStmt ast.StmtNode
   878  
   879  	Rows             [][]string
   880  	explainedCausets map[int]bool
   881  }
   882  
   883  // GetExplainRowsForCauset get explain rows for plan.
   884  func GetExplainRowsForCauset(plan Causet) (rows [][]string) {
   885  	explain := &Explain{
   886  		TargetCauset: plan,
   887  		Format:       ast.ExplainFormatROW,
   888  		Analyze:      false,
   889  	}
   890  	if err := explain.RenderResult(); err != nil {
   891  		return rows
   892  	}
   893  	return explain.Rows
   894  }
   895  
   896  // prepareSchema prepares explain's result schemaReplicant.
   897  func (e *Explain) prepareSchema() error {
   898  	var fieldNames []string
   899  	format := strings.ToLower(e.Format)
   900  
   901  	switch {
   902  	case format == ast.ExplainFormatROW && !e.Analyze:
   903  		fieldNames = []string{"id", "estRows", "task", "access object", "operator info"}
   904  	case format == ast.ExplainFormatROW && e.Analyze:
   905  		fieldNames = []string{"id", "estRows", "actRows", "task", "access object", "execution info", "operator info", "memory", "disk"}
   906  	case format == ast.ExplainFormatDOT:
   907  		fieldNames = []string{"dot contents"}
   908  	case format == ast.ExplainFormatHint:
   909  		fieldNames = []string{"hint"}
   910  	default:
   911  		return errors.Errorf("explain format '%s' is not supported now", e.Format)
   912  	}
   913  
   914  	cwn := &columnsWithNames{
   915  		defcaus: make([]*memex.DeferredCauset, 0, len(fieldNames)),
   916  		names:   make([]*types.FieldName, 0, len(fieldNames)),
   917  	}
   918  
   919  	for _, fieldName := range fieldNames {
   920  		cwn.Append(buildDeferredCausetWithName("", fieldName, allegrosql.TypeString, allegrosql.MaxBlobWidth))
   921  	}
   922  	e.SetSchema(cwn.col2Schema())
   923  	e.names = cwn.names
   924  	return nil
   925  }
   926  
   927  // RenderResult renders the explain result as specified format.
   928  func (e *Explain) RenderResult() error {
   929  	if e.TargetCauset == nil {
   930  		return nil
   931  	}
   932  	switch strings.ToLower(e.Format) {
   933  	case ast.ExplainFormatROW:
   934  		if e.Rows == nil || e.Analyze {
   935  			e.explainedCausets = map[int]bool{}
   936  			err := e.explainCausetInRowFormat(e.TargetCauset, "root", "", "", true)
   937  			if err != nil {
   938  				return err
   939  			}
   940  		}
   941  	case ast.ExplainFormatDOT:
   942  		if physicalCauset, ok := e.TargetCauset.(PhysicalCauset); ok {
   943  			e.prepareDotInfo(physicalCauset)
   944  		}
   945  	case ast.ExplainFormatHint:
   946  		hints := GenHintsFromPhysicalCauset(e.TargetCauset)
   947  		hints = append(hints, hint.ExtractBlockHintsFromStmtNode(e.InterDircStmt, nil)...)
   948  		e.Rows = append(e.Rows, []string{hint.RestoreOptimizerHints(hints)})
   949  	default:
   950  		return errors.Errorf("explain format '%s' is not supported now", e.Format)
   951  	}
   952  	return nil
   953  }
   954  
   955  // explainCausetInRowFormat generates explain information for root-tasks.
   956  func (e *Explain) explainCausetInRowFormat(p Causet, taskType, driverSide, indent string, isLastChild bool) (err error) {
   957  	e.prepareOperatorInfo(p, taskType, driverSide, indent, isLastChild)
   958  	e.explainedCausets[p.ID()] = true
   959  
   960  	// For every child we create a new sub-tree rooted by it.
   961  	childIndent := texttree.Indent4Child(indent, isLastChild)
   962  
   963  	if physCauset, ok := p.(PhysicalCauset); ok {
   964  		// indicate driven side and driving side of 'join' and 'apply'
   965  		// See issue https://github.com/whtcorpsinc/milevadb/issues/14602.
   966  		driverSideInfo := make([]string, len(physCauset.Children()))
   967  		buildSide := -1
   968  
   969  		switch plan := physCauset.(type) {
   970  		case *PhysicalApply:
   971  			buildSide = plan.InnerChildIdx ^ 1
   972  		case *PhysicalHashJoin:
   973  			if plan.UseOuterToBuild {
   974  				buildSide = plan.InnerChildIdx ^ 1
   975  			} else {
   976  				buildSide = plan.InnerChildIdx
   977  			}
   978  		case *PhysicalMergeJoin:
   979  			if plan.JoinType == RightOuterJoin {
   980  				buildSide = 0
   981  			} else {
   982  				buildSide = 1
   983  			}
   984  		case *PhysicalIndexJoin:
   985  			buildSide = plan.InnerChildIdx ^ 1
   986  		case *PhysicalIndexMergeJoin:
   987  			buildSide = plan.InnerChildIdx ^ 1
   988  		case *PhysicalIndexHashJoin:
   989  			buildSide = plan.InnerChildIdx ^ 1
   990  		case *PhysicalBroadCastJoin:
   991  			buildSide = plan.InnerChildIdx
   992  		}
   993  
   994  		if buildSide != -1 {
   995  			driverSideInfo[0], driverSideInfo[1] = "(Build)", "(Probe)"
   996  		} else {
   997  			buildSide = 0
   998  		}
   999  
  1000  		// Always put the Build above the Probe.
  1001  		for i := range physCauset.Children() {
  1002  			pchild := &physCauset.Children()[i^buildSide]
  1003  			if e.explainedCausets[(*pchild).ID()] {
  1004  				continue
  1005  			}
  1006  			err = e.explainCausetInRowFormat(*pchild, taskType, driverSideInfo[i], childIndent, i == len(physCauset.Children())-1)
  1007  			if err != nil {
  1008  				return
  1009  			}
  1010  		}
  1011  	}
  1012  
  1013  	switch x := p.(type) {
  1014  	case *PhysicalBlockReader:
  1015  		var storeType string
  1016  		switch x.StoreType {
  1017  		case ekv.EinsteinDB, ekv.TiFlash, ekv.MilevaDB:
  1018  			// expected do nothing
  1019  		default:
  1020  			return errors.Errorf("the causetstore type %v is unknown", x.StoreType)
  1021  		}
  1022  		storeType = x.StoreType.Name()
  1023  		err = e.explainCausetInRowFormat(x.blockCauset, "cop["+storeType+"]", "", childIndent, true)
  1024  	case *PhysicalIndexReader:
  1025  		err = e.explainCausetInRowFormat(x.indexCauset, "cop[einsteindb]", "", childIndent, true)
  1026  	case *PhysicalIndexLookUpReader:
  1027  		err = e.explainCausetInRowFormat(x.indexCauset, "cop[einsteindb]", "(Build)", childIndent, false)
  1028  		err = e.explainCausetInRowFormat(x.blockCauset, "cop[einsteindb]", "(Probe)", childIndent, true)
  1029  	case *PhysicalIndexMergeReader:
  1030  		for _, pchild := range x.partialCausets {
  1031  			err = e.explainCausetInRowFormat(pchild, "cop[einsteindb]", "(Build)", childIndent, false)
  1032  		}
  1033  		err = e.explainCausetInRowFormat(x.blockCauset, "cop[einsteindb]", "(Probe)", childIndent, true)
  1034  	case *Insert:
  1035  		if x.SelectCauset != nil {
  1036  			err = e.explainCausetInRowFormat(x.SelectCauset, "root", "", childIndent, true)
  1037  		}
  1038  	case *UFIDelate:
  1039  		if x.SelectCauset != nil {
  1040  			err = e.explainCausetInRowFormat(x.SelectCauset, "root", "", childIndent, true)
  1041  		}
  1042  	case *Delete:
  1043  		if x.SelectCauset != nil {
  1044  			err = e.explainCausetInRowFormat(x.SelectCauset, "root", "", childIndent, true)
  1045  		}
  1046  	case *InterDircute:
  1047  		if x.Causet != nil {
  1048  			err = e.explainCausetInRowFormat(x.Causet, "root", "", indent, true)
  1049  		}
  1050  	}
  1051  	return
  1052  }
  1053  
  1054  func getRuntimeInfo(ctx stochastikctx.Context, p Causet) (actRows, analyzeInfo, memoryInfo, diskInfo string) {
  1055  	runtimeStatsDefCausl := ctx.GetStochastikVars().StmtCtx.RuntimeStatsDefCausl
  1056  	if runtimeStatsDefCausl == nil {
  1057  		return
  1058  	}
  1059  	explainID := p.ID()
  1060  
  1061  	// There maybe some mock information for cop task to let runtimeStatsDefCausl.Exists(p.ExplainID()) is true.
  1062  	// So check copTaskEkxecDetail first and print the real cop task information if it's not empty.
  1063  	if runtimeStatsDefCausl.ExistsCopStats(explainID) {
  1064  		copstats := runtimeStatsDefCausl.GetCopStats(explainID)
  1065  		analyzeInfo = copstats.String()
  1066  		actRows = fmt.Sprint(copstats.GetActRows())
  1067  	} else if runtimeStatsDefCausl.ExistsRootStats(explainID) {
  1068  		rootstats := runtimeStatsDefCausl.GetRootStats(explainID)
  1069  		analyzeInfo = rootstats.String()
  1070  		actRows = fmt.Sprint(rootstats.GetActRows())
  1071  	} else {
  1072  		analyzeInfo = "time:0ns, loops:0"
  1073  		actRows = "0"
  1074  	}
  1075  
  1076  	memoryInfo = "N/A"
  1077  	memTracker := ctx.GetStochastikVars().StmtCtx.MemTracker.SearchTrackerWithoutLock(p.ID())
  1078  	if memTracker != nil {
  1079  		memoryInfo = memTracker.BytesToString(memTracker.MaxConsumed())
  1080  	}
  1081  
  1082  	diskInfo = "N/A"
  1083  	diskTracker := ctx.GetStochastikVars().StmtCtx.DiskTracker.SearchTrackerWithoutLock(p.ID())
  1084  	if diskTracker != nil {
  1085  		diskInfo = diskTracker.BytesToString(diskTracker.MaxConsumed())
  1086  	}
  1087  	return
  1088  }
  1089  
  1090  // prepareOperatorInfo generates the following information for every plan:
  1091  // operator id, estimated rows, task type, access object and other operator info.
  1092  func (e *Explain) prepareOperatorInfo(p Causet, taskType, driverSide, indent string, isLastChild bool) {
  1093  	if p.ExplainID().String() == "_0" {
  1094  		return
  1095  	}
  1096  
  1097  	id := texttree.PrettyIdentifier(p.ExplainID().String()+driverSide, indent, isLastChild)
  1098  
  1099  	estRows := "N/A"
  1100  	if si := p.statsInfo(); si != nil {
  1101  		estRows = strconv.FormatFloat(si.RowCount, 'f', 2, 64)
  1102  	}
  1103  
  1104  	var accessObject, operatorInfo string
  1105  	if plan, ok := p.(dataAccesser); ok {
  1106  		accessObject = plan.AccessObject()
  1107  		operatorInfo = plan.OperatorInfo(false)
  1108  	} else {
  1109  		if pa, ok := p.(partitionAccesser); ok && e.ctx != nil {
  1110  			accessObject = pa.accessObject(e.ctx)
  1111  		}
  1112  		operatorInfo = p.ExplainInfo()
  1113  	}
  1114  
  1115  	var event []string
  1116  	if e.Analyze {
  1117  		actRows, analyzeInfo, memoryInfo, diskInfo := getRuntimeInfo(e.ctx, p)
  1118  		event = []string{id, estRows, actRows, taskType, accessObject, analyzeInfo, operatorInfo, memoryInfo, diskInfo}
  1119  	} else {
  1120  		event = []string{id, estRows, taskType, accessObject, operatorInfo}
  1121  	}
  1122  	e.Rows = append(e.Rows, event)
  1123  }
  1124  
  1125  func (e *Explain) prepareDotInfo(p PhysicalCauset) {
  1126  	buffer := bytes.NewBufferString("")
  1127  	fmt.Fprintf(buffer, "\ndigraph %s {\n", p.ExplainID())
  1128  	e.prepareTaskDot(p, "root", buffer)
  1129  	buffer.WriteString("}\n")
  1130  
  1131  	e.Rows = append(e.Rows, []string{buffer.String()})
  1132  }
  1133  
  1134  func (e *Explain) prepareTaskDot(p PhysicalCauset, taskTp string, buffer *bytes.Buffer) {
  1135  	fmt.Fprintf(buffer, "subgraph cluster%v{\n", p.ID())
  1136  	buffer.WriteString("node [style=filled, color=lightgrey]\n")
  1137  	buffer.WriteString("color=black\n")
  1138  	fmt.Fprintf(buffer, "label = \"%s\"\n", taskTp)
  1139  
  1140  	if len(p.Children()) == 0 {
  1141  		if taskTp == "cop" {
  1142  			fmt.Fprintf(buffer, "\"%s\"\n}\n", p.ExplainID())
  1143  			return
  1144  		}
  1145  		fmt.Fprintf(buffer, "\"%s\"\n", p.ExplainID())
  1146  	}
  1147  
  1148  	var CausetTasks []PhysicalCauset
  1149  	var pipelines []string
  1150  
  1151  	for planQueue := []PhysicalCauset{p}; len(planQueue) > 0; planQueue = planQueue[1:] {
  1152  		curCauset := planQueue[0]
  1153  		switch copCauset := curCauset.(type) {
  1154  		case *PhysicalBlockReader:
  1155  			pipelines = append(pipelines, fmt.Sprintf("\"%s\" -> \"%s\"\n", copCauset.ExplainID(), copCauset.blockCauset.ExplainID()))
  1156  			CausetTasks = append(CausetTasks, copCauset.blockCauset)
  1157  		case *PhysicalIndexReader:
  1158  			pipelines = append(pipelines, fmt.Sprintf("\"%s\" -> \"%s\"\n", copCauset.ExplainID(), copCauset.indexCauset.ExplainID()))
  1159  			CausetTasks = append(CausetTasks, copCauset.indexCauset)
  1160  		case *PhysicalIndexLookUpReader:
  1161  			pipelines = append(pipelines, fmt.Sprintf("\"%s\" -> \"%s\"\n", copCauset.ExplainID(), copCauset.blockCauset.ExplainID()))
  1162  			pipelines = append(pipelines, fmt.Sprintf("\"%s\" -> \"%s\"\n", copCauset.ExplainID(), copCauset.indexCauset.ExplainID()))
  1163  			CausetTasks = append(CausetTasks, copCauset.blockCauset)
  1164  			CausetTasks = append(CausetTasks, copCauset.indexCauset)
  1165  		case *PhysicalIndexMergeReader:
  1166  			for i := 0; i < len(copCauset.partialCausets); i++ {
  1167  				pipelines = append(pipelines, fmt.Sprintf("\"%s\" -> \"%s\"\n", copCauset.ExplainID(), copCauset.partialCausets[i].ExplainID()))
  1168  				CausetTasks = append(CausetTasks, copCauset.partialCausets[i])
  1169  			}
  1170  			if copCauset.blockCauset != nil {
  1171  				pipelines = append(pipelines, fmt.Sprintf("\"%s\" -> \"%s\"\n", copCauset.ExplainID(), copCauset.blockCauset.ExplainID()))
  1172  				CausetTasks = append(CausetTasks, copCauset.blockCauset)
  1173  			}
  1174  		}
  1175  		for _, child := range curCauset.Children() {
  1176  			fmt.Fprintf(buffer, "\"%s\" -> \"%s\"\n", curCauset.ExplainID(), child.ExplainID())
  1177  			planQueue = append(planQueue, child)
  1178  		}
  1179  	}
  1180  	buffer.WriteString("}\n")
  1181  
  1182  	for _, cop := range CausetTasks {
  1183  		e.prepareTaskDot(cop.(PhysicalCauset), "cop", buffer)
  1184  	}
  1185  
  1186  	for i := range pipelines {
  1187  		buffer.WriteString(pipelines[i])
  1188  	}
  1189  }
  1190  
  1191  // IsPointGetWithPKOrUniqueKeyByAutoCommit returns true when meets following conditions:
  1192  //  1. ctx is auto commit tagged
  1193  //  2. stochastik is not InTxn
  1194  //  3. plan is point get by pk, or point get by unique index (no double read)
  1195  func IsPointGetWithPKOrUniqueKeyByAutoCommit(ctx stochastikctx.Context, p Causet) (bool, error) {
  1196  	if !IsAutoCommitTxn(ctx) {
  1197  		return false, nil
  1198  	}
  1199  
  1200  	// check plan
  1201  	if proj, ok := p.(*PhysicalProjection); ok {
  1202  		p = proj.Children()[0]
  1203  	}
  1204  
  1205  	switch v := p.(type) {
  1206  	case *PhysicalIndexReader:
  1207  		indexScan := v.IndexCausets[0].(*PhysicalIndexScan)
  1208  		return indexScan.IsPointGetByUniqueKey(ctx.GetStochastikVars().StmtCtx), nil
  1209  	case *PhysicalBlockReader:
  1210  		blockScan := v.BlockCausets[0].(*PhysicalBlockScan)
  1211  		isPointRange := len(blockScan.Ranges) == 1 && blockScan.Ranges[0].IsPoint(ctx.GetStochastikVars().StmtCtx)
  1212  		if !isPointRange {
  1213  			return false, nil
  1214  		}
  1215  		pkLength := 1
  1216  		if blockScan.Block.IsCommonHandle {
  1217  			pkIdx := blocks.FindPrimaryIndex(blockScan.Block)
  1218  			pkLength = len(pkIdx.DeferredCausets)
  1219  		}
  1220  		return len(blockScan.Ranges[0].LowVal) == pkLength, nil
  1221  	case *PointGetCauset:
  1222  		// If the PointGetCauset needs to read data using unique index (double read), we
  1223  		// can't use max uint64, because using math.MaxUint64 can't guarantee repeablock-read
  1224  		// and the data and index would be inconsistent!
  1225  		isPointGet := v.IndexInfo == nil || (v.IndexInfo.Primary && v.TblInfo.IsCommonHandle)
  1226  		return isPointGet, nil
  1227  	default:
  1228  		return false, nil
  1229  	}
  1230  }
  1231  
  1232  // IsAutoCommitTxn checks if stochastik is in autocommit mode and not InTxn
  1233  // used for fast plan like point get
  1234  func IsAutoCommitTxn(ctx stochastikctx.Context) bool {
  1235  	return ctx.GetStochastikVars().IsAutocommit() && !ctx.GetStochastikVars().InTxn()
  1236  }
  1237  
  1238  // IsPointUFIDelateByAutoCommit checks if plan p is point uFIDelate and is in autocommit context
  1239  func IsPointUFIDelateByAutoCommit(ctx stochastikctx.Context, p Causet) (bool, error) {
  1240  	if !IsAutoCommitTxn(ctx) {
  1241  		return false, nil
  1242  	}
  1243  
  1244  	// check plan
  1245  	uFIDelCauset, ok := p.(*UFIDelate)
  1246  	if !ok {
  1247  		return false, nil
  1248  	}
  1249  	if _, isFastSel := uFIDelCauset.SelectCauset.(*PointGetCauset); isFastSel {
  1250  		return true, nil
  1251  	}
  1252  	return false, nil
  1253  }
  1254  
  1255  func buildSchemaAndNameFromIndex(defcaus []*memex.DeferredCauset, dbName perceptron.CIStr, tblInfo *perceptron.BlockInfo, idxInfo *perceptron.IndexInfo) (*memex.Schema, types.NameSlice) {
  1256  	schemaReplicant := memex.NewSchema(defcaus...)
  1257  	idxDefCauss := idxInfo.DeferredCausets
  1258  	names := make([]*types.FieldName, 0, len(idxDefCauss))
  1259  	tblName := tblInfo.Name
  1260  	for _, col := range idxDefCauss {
  1261  		names = append(names, &types.FieldName{
  1262  			OrigTblName:     tblName,
  1263  			OrigDefCausName: col.Name,
  1264  			DBName:          dbName,
  1265  			TblName:         tblName,
  1266  			DefCausName:     col.Name,
  1267  		})
  1268  	}
  1269  	return schemaReplicant, names
  1270  }
  1271  
  1272  func buildSchemaAndNameFromPKDefCaus(pkDefCaus *memex.DeferredCauset, dbName perceptron.CIStr, tblInfo *perceptron.BlockInfo) (*memex.Schema, types.NameSlice) {
  1273  	schemaReplicant := memex.NewSchema([]*memex.DeferredCauset{pkDefCaus}...)
  1274  	names := make([]*types.FieldName, 0, 1)
  1275  	tblName := tblInfo.Name
  1276  	col := tblInfo.GetPkDefCausInfo()
  1277  	names = append(names, &types.FieldName{
  1278  		OrigTblName:     tblName,
  1279  		OrigDefCausName: col.Name,
  1280  		DBName:          dbName,
  1281  		TblName:         tblName,
  1282  		DefCausName:     col.Name,
  1283  	})
  1284  	return schemaReplicant, names
  1285  }
  1286  
  1287  func locateHashPartition(ctx stochastikctx.Context, expr memex.Expression, pi *perceptron.PartitionInfo, r []types.Causet) (int, error) {
  1288  	ret, isNull, err := expr.EvalInt(ctx, chunk.MutRowFromCausets(r).ToRow())
  1289  	if err != nil {
  1290  		return 0, err
  1291  	}
  1292  	if isNull {
  1293  		return 0, nil
  1294  	}
  1295  	if ret < 0 {
  1296  		ret = 0 - ret
  1297  	}
  1298  	return int(ret % int64(pi.Num)), nil
  1299  }