github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/interlock/table_reader.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 interlock
    15  
    16  import (
    17  	"context"
    18  	"sort"
    19  
    20  	"github.com/opentracing/opentracing-go"
    21  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    22  	"github.com/whtcorpsinc/fidelpb/go-fidelpb"
    23  	"github.com/whtcorpsinc/milevadb/allegrosql"
    24  	"github.com/whtcorpsinc/milevadb/causet"
    25  	causetembedded "github.com/whtcorpsinc/milevadb/causet/embedded"
    26  	"github.com/whtcorpsinc/milevadb/ekv"
    27  	"github.com/whtcorpsinc/milevadb/memex"
    28  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    29  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    30  	"github.com/whtcorpsinc/milevadb/soliton/memory"
    31  	"github.com/whtcorpsinc/milevadb/soliton/ranger"
    32  	"github.com/whtcorpsinc/milevadb/soliton/stringutil"
    33  	"github.com/whtcorpsinc/milevadb/statistics"
    34  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    35  	"github.com/whtcorpsinc/milevadb/types"
    36  )
    37  
    38  // make sure `BlockReaderInterlockingDirectorate` implements `InterlockingDirectorate`.
    39  var _ InterlockingDirectorate = &BlockReaderInterlockingDirectorate{}
    40  
    41  // selectResultHook is used to replog allegrosql.SelectWithRuntimeStats safely for testing.
    42  type selectResultHook struct {
    43  	selectResultFunc func(ctx context.Context, sctx stochastikctx.Context, ekvReq *ekv.Request,
    44  		fieldTypes []*types.FieldType, fb *statistics.QueryFeedback, copCausetIDs []int) (allegrosql.SelectResult, error)
    45  }
    46  
    47  func (sr selectResultHook) SelectResult(ctx context.Context, sctx stochastikctx.Context, ekvReq *ekv.Request,
    48  	fieldTypes []*types.FieldType, fb *statistics.QueryFeedback, copCausetIDs []int, rootCausetID int) (allegrosql.SelectResult, error) {
    49  	if sr.selectResultFunc == nil {
    50  		return allegrosql.SelectWithRuntimeStats(ctx, sctx, ekvReq, fieldTypes, fb, copCausetIDs, rootCausetID)
    51  	}
    52  	return sr.selectResultFunc(ctx, sctx, ekvReq, fieldTypes, fb, copCausetIDs)
    53  }
    54  
    55  type ekvRangeBuilder interface {
    56  	buildKeyRange(pid int64) ([]ekv.KeyRange, error)
    57  }
    58  
    59  // BlockReaderInterlockingDirectorate sends PosetDag request and reads causet data from ekv layer.
    60  type BlockReaderInterlockingDirectorate struct {
    61  	baseInterlockingDirectorate
    62  
    63  	causet causet.Block
    64  
    65  	// The source of key ranges varies from case to case.
    66  	// It may be calculated from PyhsicalCauset by interlockBuilder, or calculated from argument by dataBuilder;
    67  	// It may be calculated from ranger.Ranger, or calculated from handles.
    68  	// The causet ID may also change because of the partition causet, and causes the key range to change.
    69  	// So instead of keeping a `range` struct field, it's better to define a interface.
    70  	ekvRangeBuilder
    71  	// TODO: remove this field, use the ekvRangeBuilder interface.
    72  	ranges []*ranger.Range
    73  
    74  	// ekvRanges are only use for union scan.
    75  	ekvRanges       []ekv.KeyRange
    76  	posetPosetDagPB *fidelpb.PosetDagRequest
    77  	startTS         uint64
    78  	// defCausumns are only required by union scan and virtual defCausumn.
    79  	defCausumns []*perceptron.DeferredCausetInfo
    80  
    81  	// resultHandler handles the order of the result. Since (MAXInt64, MAXUint64] stores before [0, MaxInt64] physically
    82  	// for unsigned int.
    83  	resultHandler *blockResultHandler
    84  	feedback      *statistics.QueryFeedback
    85  	plans         []causetembedded.PhysicalCauset
    86  	blockCauset   causetembedded.PhysicalCauset
    87  
    88  	memTracker       *memory.Tracker
    89  	selectResultHook // for testing
    90  
    91  	keepOrder bool
    92  	desc      bool
    93  	streaming bool
    94  	storeType ekv.StoreType
    95  	// corDefCausInFilter tells whether there's correlated defCausumn in filter.
    96  	corDefCausInFilter bool
    97  	// corDefCausInAccess tells whether there's correlated defCausumn in access conditions.
    98  	corDefCausInAccess bool
    99  	// virtualDeferredCausetIndex records all the indices of virtual defCausumns and sort them in definition
   100  	// to make sure we can compute the virtual defCausumn in right order.
   101  	virtualDeferredCausetIndex []int
   102  	// virtualDeferredCausetRetFieldTypes records the RetFieldTypes of virtual defCausumns.
   103  	virtualDeferredCausetRetFieldTypes []*types.FieldType
   104  	// batchCop indicates whether use super batch interlock request, only works for TiFlash engine.
   105  	batchCop bool
   106  }
   107  
   108  // Open initialzes necessary variables for using this interlock.
   109  func (e *BlockReaderInterlockingDirectorate) Open(ctx context.Context) error {
   110  	if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil {
   111  		span1 := span.Tracer().StartSpan("BlockReaderInterlockingDirectorate.Open", opentracing.ChildOf(span.Context()))
   112  		defer span1.Finish()
   113  		ctx = opentracing.ContextWithSpan(ctx, span1)
   114  	}
   115  
   116  	e.memTracker = memory.NewTracker(e.id, -1)
   117  	e.memTracker.AttachTo(e.ctx.GetStochastikVars().StmtCtx.MemTracker)
   118  
   119  	var err error
   120  	if e.corDefCausInFilter {
   121  		if e.storeType == ekv.TiFlash {
   122  			execs, _, err := constructDistInterDircForTiFlash(e.ctx, e.blockCauset)
   123  			if err != nil {
   124  				return err
   125  			}
   126  			e.posetPosetDagPB.RootInterlockingDirectorate = execs[0]
   127  		} else {
   128  			e.posetPosetDagPB.InterlockingDirectorates, _, err = constructDistInterDirc(e.ctx, e.plans)
   129  			if err != nil {
   130  				return err
   131  			}
   132  		}
   133  	}
   134  	if e.runtimeStats != nil {
   135  		defCauslInterDirc := true
   136  		e.posetPosetDagPB.DefCauslectInterDircutionSummaries = &defCauslInterDirc
   137  	}
   138  	if e.corDefCausInAccess {
   139  		ts := e.plans[0].(*causetembedded.PhysicalBlockScan)
   140  		access := ts.AccessCondition
   141  		pkTP := ts.Block.GetPkDefCausInfo().FieldType
   142  		e.ranges, err = ranger.BuildBlockRange(access, e.ctx.GetStochastikVars().StmtCtx, &pkTP)
   143  		if err != nil {
   144  			return err
   145  		}
   146  	}
   147  
   148  	e.resultHandler = &blockResultHandler{}
   149  	if e.feedback != nil && e.feedback.Hist != nil {
   150  		// EncodeInt don't need *memex.Context.
   151  		var ok bool
   152  		e.ranges, ok = e.feedback.Hist.SplitRange(nil, e.ranges, false)
   153  		if !ok {
   154  			e.feedback.Invalidate()
   155  		}
   156  	}
   157  	firstPartRanges, secondPartRanges := splitRanges(e.ranges, e.keepOrder, e.desc)
   158  	firstResult, err := e.buildResp(ctx, firstPartRanges)
   159  	if err != nil {
   160  		e.feedback.Invalidate()
   161  		return err
   162  	}
   163  	if len(secondPartRanges) == 0 {
   164  		e.resultHandler.open(nil, firstResult)
   165  		return nil
   166  	}
   167  	var secondResult allegrosql.SelectResult
   168  	secondResult, err = e.buildResp(ctx, secondPartRanges)
   169  	if err != nil {
   170  		e.feedback.Invalidate()
   171  		return err
   172  	}
   173  	e.resultHandler.open(firstResult, secondResult)
   174  	return nil
   175  }
   176  
   177  // Next fills data into the chunk passed by its caller.
   178  // The task was actually done by blockReaderHandler.
   179  func (e *BlockReaderInterlockingDirectorate) Next(ctx context.Context, req *chunk.Chunk) error {
   180  	logutil.Eventf(ctx, "causet scan causet: %s, range: %v", stringutil.MemoizeStr(func() string {
   181  		var blockName string
   182  		if spacetime := e.causet.Meta(); spacetime != nil {
   183  			blockName = spacetime.Name.L
   184  		}
   185  		return blockName
   186  	}), e.ranges)
   187  	if err := e.resultHandler.nextChunk(ctx, req); err != nil {
   188  		e.feedback.Invalidate()
   189  		return err
   190  	}
   191  
   192  	err := FillVirtualDeferredCausetValue(e.virtualDeferredCausetRetFieldTypes, e.virtualDeferredCausetIndex, e.schemaReplicant, e.defCausumns, e.ctx, req)
   193  	if err != nil {
   194  		return err
   195  	}
   196  
   197  	return nil
   198  }
   199  
   200  // Close implements the InterlockingDirectorate Close interface.
   201  func (e *BlockReaderInterlockingDirectorate) Close() error {
   202  	var err error
   203  	if e.resultHandler != nil {
   204  		err = e.resultHandler.Close()
   205  	}
   206  	e.ekvRanges = e.ekvRanges[:0]
   207  	e.ctx.StoreQueryFeedback(e.feedback)
   208  	return err
   209  }
   210  
   211  // buildResp first builds request and sends it to einsteindb using allegrosql.Select. It uses SelectResut returned by the callee
   212  // to fetch all results.
   213  func (e *BlockReaderInterlockingDirectorate) buildResp(ctx context.Context, ranges []*ranger.Range) (allegrosql.SelectResult, error) {
   214  	var builder allegrosql.RequestBuilder
   215  	var reqBuilder *allegrosql.RequestBuilder
   216  	if e.ekvRangeBuilder != nil {
   217  		ekvRange, err := e.ekvRangeBuilder.buildKeyRange(getPhysicalBlockID(e.causet))
   218  		if err != nil {
   219  			return nil, err
   220  		}
   221  		reqBuilder = builder.SetKeyRanges(ekvRange)
   222  	} else if e.causet.Meta() != nil && e.causet.Meta().IsCommonHandle {
   223  		reqBuilder = builder.SetCommonHandleRanges(e.ctx.GetStochastikVars().StmtCtx, getPhysicalBlockID(e.causet), ranges)
   224  	} else {
   225  		reqBuilder = builder.SetBlockRanges(getPhysicalBlockID(e.causet), ranges, e.feedback)
   226  	}
   227  	ekvReq, err := reqBuilder.
   228  		SetPosetDagRequest(e.posetPosetDagPB).
   229  		SetStartTS(e.startTS).
   230  		SetDesc(e.desc).
   231  		SetKeepOrder(e.keepOrder).
   232  		SetStreaming(e.streaming).
   233  		SetFromStochastikVars(e.ctx.GetStochastikVars()).
   234  		SetMemTracker(e.memTracker).
   235  		SetStoreType(e.storeType).
   236  		SetAllowBatchCop(e.batchCop).
   237  		Build()
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  	e.ekvRanges = append(e.ekvRanges, ekvReq.KeyRanges...)
   242  
   243  	result, err := e.SelectResult(ctx, e.ctx, ekvReq, retTypes(e), e.feedback, getPhysicalCausetIDs(e.plans), e.id)
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  	result.Fetch(ctx)
   248  	return result, nil
   249  }
   250  
   251  func buildVirtualDeferredCausetIndex(schemaReplicant *memex.Schema, defCausumns []*perceptron.DeferredCausetInfo) []int {
   252  	virtualDeferredCausetIndex := make([]int, 0, len(defCausumns))
   253  	for i, defCaus := range schemaReplicant.DeferredCausets {
   254  		if defCaus.VirtualExpr != nil {
   255  			virtualDeferredCausetIndex = append(virtualDeferredCausetIndex, i)
   256  		}
   257  	}
   258  	sort.Slice(virtualDeferredCausetIndex, func(i, j int) bool {
   259  		return causetembedded.FindDeferredCausetInfoByID(defCausumns, schemaReplicant.DeferredCausets[virtualDeferredCausetIndex[i]].ID).Offset <
   260  			causetembedded.FindDeferredCausetInfoByID(defCausumns, schemaReplicant.DeferredCausets[virtualDeferredCausetIndex[j]].ID).Offset
   261  	})
   262  	return virtualDeferredCausetIndex
   263  }
   264  
   265  // buildVirtualDeferredCausetInfo saves virtual defCausumn indices and sort them in definition order
   266  func (e *BlockReaderInterlockingDirectorate) buildVirtualDeferredCausetInfo() {
   267  	e.virtualDeferredCausetIndex = buildVirtualDeferredCausetIndex(e.Schema(), e.defCausumns)
   268  	if len(e.virtualDeferredCausetIndex) > 0 {
   269  		e.virtualDeferredCausetRetFieldTypes = make([]*types.FieldType, len(e.virtualDeferredCausetIndex))
   270  		for i, idx := range e.virtualDeferredCausetIndex {
   271  			e.virtualDeferredCausetRetFieldTypes[i] = e.schemaReplicant.DeferredCausets[idx].RetType
   272  		}
   273  	}
   274  }
   275  
   276  type blockResultHandler struct {
   277  	// If the pk is unsigned and we have KeepOrder=true and want ascending order,
   278  	// `optionalResult` will handles the request whose range is in signed int range, and
   279  	// `result` will handle the request whose range is exceed signed int range.
   280  	// If we want descending order, `optionalResult` will handles the request whose range is exceed signed, and
   281  	// the `result` will handle the request whose range is in signed.
   282  	// Otherwise, we just set `optionalFinished` true and the `result` handles the whole ranges.
   283  	optionalResult allegrosql.SelectResult
   284  	result         allegrosql.SelectResult
   285  
   286  	optionalFinished bool
   287  }
   288  
   289  func (tr *blockResultHandler) open(optionalResult, result allegrosql.SelectResult) {
   290  	if optionalResult == nil {
   291  		tr.optionalFinished = true
   292  		tr.result = result
   293  		return
   294  	}
   295  	tr.optionalResult = optionalResult
   296  	tr.result = result
   297  	tr.optionalFinished = false
   298  }
   299  
   300  func (tr *blockResultHandler) nextChunk(ctx context.Context, chk *chunk.Chunk) error {
   301  	if !tr.optionalFinished {
   302  		err := tr.optionalResult.Next(ctx, chk)
   303  		if err != nil {
   304  			return err
   305  		}
   306  		if chk.NumEvents() > 0 {
   307  			return nil
   308  		}
   309  		tr.optionalFinished = true
   310  	}
   311  	return tr.result.Next(ctx, chk)
   312  }
   313  
   314  func (tr *blockResultHandler) nextRaw(ctx context.Context) (data []byte, err error) {
   315  	if !tr.optionalFinished {
   316  		data, err = tr.optionalResult.NextRaw(ctx)
   317  		if err != nil {
   318  			return nil, err
   319  		}
   320  		if data != nil {
   321  			return data, nil
   322  		}
   323  		tr.optionalFinished = true
   324  	}
   325  	data, err = tr.result.NextRaw(ctx)
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	return data, nil
   330  }
   331  
   332  func (tr *blockResultHandler) Close() error {
   333  	err := closeAll(tr.optionalResult, tr.result)
   334  	tr.optionalResult, tr.result = nil, nil
   335  	return err
   336  }