github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/interlock/union_scan.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  	"fmt"
    19  	"runtime/trace"
    20  
    21  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    22  	"github.com/whtcorpsinc/milevadb/causet"
    23  	causetembedded "github.com/whtcorpsinc/milevadb/causet/embedded"
    24  	"github.com/whtcorpsinc/milevadb/ekv"
    25  	"github.com/whtcorpsinc/milevadb/memex"
    26  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    27  	"github.com/whtcorpsinc/milevadb/types"
    28  )
    29  
    30  // UnionScanInterDirc merges the rows from dirty causet and the rows from allegrosql request.
    31  type UnionScanInterDirc struct {
    32  	baseInterlockingDirectorate
    33  
    34  	memBuf     ekv.MemBuffer
    35  	memBufSnap ekv.Getter
    36  
    37  	// usedIndex is the defCausumn offsets of the index which Src interlock has used.
    38  	usedIndex                []int
    39  	desc                     bool
    40  	conditions               []memex.Expression
    41  	conditionsWithVirDefCaus []memex.Expression
    42  	defCausumns              []*perceptron.DeferredCausetInfo
    43  	causet                   causet.Block
    44  	// belowHandleDefCauss is the handle's position of the below scan plan.
    45  	belowHandleDefCauss causetembedded.HandleDefCauss
    46  
    47  	addedEvents           [][]types.Causet
    48  	cursor4AddEvents      int
    49  	sortErr               error
    50  	snapshotEvents        [][]types.Causet
    51  	cursor4SnapshotEvents int
    52  	snapshotChunkBuffer   *chunk.Chunk
    53  	mublockEvent          chunk.MutEvent
    54  	// virtualDeferredCausetIndex records all the indices of virtual defCausumns and sort them in definition
    55  	// to make sure we can compute the virtual defCausumn in right order.
    56  	virtualDeferredCausetIndex []int
    57  }
    58  
    59  // Open implements the InterlockingDirectorate Open interface.
    60  func (us *UnionScanInterDirc) Open(ctx context.Context) error {
    61  	if err := us.baseInterlockingDirectorate.Open(ctx); err != nil {
    62  		return err
    63  	}
    64  	return us.open(ctx)
    65  }
    66  
    67  func (us *UnionScanInterDirc) open(ctx context.Context) error {
    68  	var err error
    69  	reader := us.children[0]
    70  
    71  	// If the push-downed condition contains virtual defCausumn, we may build a selection upon reader. Since unionScanInterDirc
    72  	// has already contained condition, we can ignore the selection.
    73  	if sel, ok := reader.(*SelectionInterDirc); ok {
    74  		reader = sel.children[0]
    75  	}
    76  
    77  	defer trace.StartRegion(ctx, "UnionScanBuildEvents").End()
    78  	txn, err := us.ctx.Txn(false)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	mb := txn.GetMemBuffer()
    84  	mb.RLock()
    85  	defer mb.RUnlock()
    86  
    87  	us.memBuf = mb
    88  	us.memBufSnap = mb.SnapshotGetter()
    89  
    90  	// 1. select without virtual defCausumns
    91  	// 2. build virtual defCausumns and select with virtual defCausumns
    92  	switch x := reader.(type) {
    93  	case *BlockReaderInterlockingDirectorate:
    94  		us.addedEvents, err = buildMemBlockReader(us, x).getMemEvents()
    95  	case *IndexReaderInterlockingDirectorate:
    96  		us.addedEvents, err = buildMemIndexReader(us, x).getMemEvents()
    97  	case *IndexLookUpInterlockingDirectorate:
    98  		us.addedEvents, err = buildMemIndexLookUpReader(us, x).getMemEvents()
    99  	default:
   100  		err = fmt.Errorf("unexpected union scan children:%T", reader)
   101  	}
   102  	if err != nil {
   103  		return err
   104  	}
   105  	us.snapshotChunkBuffer = newFirstChunk(us)
   106  	return nil
   107  }
   108  
   109  // Next implements the InterlockingDirectorate Next interface.
   110  func (us *UnionScanInterDirc) Next(ctx context.Context, req *chunk.Chunk) error {
   111  	us.memBuf.RLock()
   112  	defer us.memBuf.RUnlock()
   113  	req.GrowAndReset(us.maxChunkSize)
   114  	mublockEvent := chunk.MutEventFromTypes(retTypes(us))
   115  	for i, batchSize := 0, req.Capacity(); i < batchSize; i++ {
   116  		event, err := us.getOneEvent(ctx)
   117  		if err != nil {
   118  			return err
   119  		}
   120  		// no more data.
   121  		if event == nil {
   122  			return nil
   123  		}
   124  		mublockEvent.SetCausets(event...)
   125  
   126  		for _, idx := range us.virtualDeferredCausetIndex {
   127  			causet, err := us.schemaReplicant.DeferredCausets[idx].EvalVirtualDeferredCauset(mublockEvent.ToEvent())
   128  			if err != nil {
   129  				return err
   130  			}
   131  			// Because the memex might return different type from
   132  			// the generated defCausumn, we should wrap a CAST on the result.
   133  			castCauset, err := causet.CastValue(us.ctx, causet, us.defCausumns[idx], false, true)
   134  			if err != nil {
   135  				return err
   136  			}
   137  			mublockEvent.SetCauset(idx, castCauset)
   138  		}
   139  
   140  		matched, _, err := memex.EvalBool(us.ctx, us.conditionsWithVirDefCaus, mublockEvent.ToEvent())
   141  		if err != nil {
   142  			return err
   143  		}
   144  		if matched {
   145  			req.AppendEvent(mublockEvent.ToEvent())
   146  		}
   147  	}
   148  	return nil
   149  }
   150  
   151  // Close implements the InterlockingDirectorate Close interface.
   152  func (us *UnionScanInterDirc) Close() error {
   153  	us.cursor4AddEvents = 0
   154  	us.cursor4SnapshotEvents = 0
   155  	us.addedEvents = us.addedEvents[:0]
   156  	us.snapshotEvents = us.snapshotEvents[:0]
   157  	return us.children[0].Close()
   158  }
   159  
   160  // getOneEvent gets one result event from dirty causet or child.
   161  func (us *UnionScanInterDirc) getOneEvent(ctx context.Context) ([]types.Causet, error) {
   162  	snapshotEvent, err := us.getSnapshotEvent(ctx)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	addedEvent := us.getAddedEvent()
   167  
   168  	var event []types.Causet
   169  	var isSnapshotEvent bool
   170  	if addedEvent == nil {
   171  		event = snapshotEvent
   172  		isSnapshotEvent = true
   173  	} else if snapshotEvent == nil {
   174  		event = addedEvent
   175  	} else {
   176  		isSnapshotEvent, err = us.shouldPickFirstEvent(snapshotEvent, addedEvent)
   177  		if err != nil {
   178  			return nil, err
   179  		}
   180  		if isSnapshotEvent {
   181  			event = snapshotEvent
   182  		} else {
   183  			event = addedEvent
   184  		}
   185  	}
   186  	if event == nil {
   187  		return nil, nil
   188  	}
   189  
   190  	if isSnapshotEvent {
   191  		us.cursor4SnapshotEvents++
   192  	} else {
   193  		us.cursor4AddEvents++
   194  	}
   195  	return event, nil
   196  }
   197  
   198  func (us *UnionScanInterDirc) getSnapshotEvent(ctx context.Context) ([]types.Causet, error) {
   199  	if us.cursor4SnapshotEvents < len(us.snapshotEvents) {
   200  		return us.snapshotEvents[us.cursor4SnapshotEvents], nil
   201  	}
   202  	var err error
   203  	us.cursor4SnapshotEvents = 0
   204  	us.snapshotEvents = us.snapshotEvents[:0]
   205  	for len(us.snapshotEvents) == 0 {
   206  		err = Next(ctx, us.children[0], us.snapshotChunkBuffer)
   207  		if err != nil || us.snapshotChunkBuffer.NumEvents() == 0 {
   208  			return nil, err
   209  		}
   210  		iter := chunk.NewIterator4Chunk(us.snapshotChunkBuffer)
   211  		for event := iter.Begin(); event != iter.End(); event = iter.Next() {
   212  			var snapshotHandle ekv.Handle
   213  			snapshotHandle, err = us.belowHandleDefCauss.BuildHandle(event)
   214  			if err != nil {
   215  				return nil, err
   216  			}
   217  			checkKey := us.causet.RecordKey(snapshotHandle)
   218  			if _, err := us.memBufSnap.Get(context.TODO(), checkKey); err == nil {
   219  				// If src handle appears in added rows, it means there is conflict and the transaction will fail to
   220  				// commit, but for simplicity, we don't handle it here.
   221  				continue
   222  			}
   223  			us.snapshotEvents = append(us.snapshotEvents, event.GetCausetEvent(retTypes(us.children[0])))
   224  		}
   225  	}
   226  	return us.snapshotEvents[0], nil
   227  }
   228  
   229  func (us *UnionScanInterDirc) getAddedEvent() []types.Causet {
   230  	var addedEvent []types.Causet
   231  	if us.cursor4AddEvents < len(us.addedEvents) {
   232  		addedEvent = us.addedEvents[us.cursor4AddEvents]
   233  	}
   234  	return addedEvent
   235  }
   236  
   237  // shouldPickFirstEvent picks the suiblock event in order.
   238  // The value returned is used to determine whether to pick the first input event.
   239  func (us *UnionScanInterDirc) shouldPickFirstEvent(a, b []types.Causet) (bool, error) {
   240  	var isFirstEvent bool
   241  	addedCmpSrc, err := us.compare(a, b)
   242  	if err != nil {
   243  		return isFirstEvent, err
   244  	}
   245  	// Compare result will never be 0.
   246  	if us.desc {
   247  		if addedCmpSrc > 0 {
   248  			isFirstEvent = true
   249  		}
   250  	} else {
   251  		if addedCmpSrc < 0 {
   252  			isFirstEvent = true
   253  		}
   254  	}
   255  	return isFirstEvent, nil
   256  }
   257  
   258  func (us *UnionScanInterDirc) compare(a, b []types.Causet) (int, error) {
   259  	sc := us.ctx.GetStochastikVars().StmtCtx
   260  	for _, defCausOff := range us.usedIndex {
   261  		aDeferredCauset := a[defCausOff]
   262  		bDeferredCauset := b[defCausOff]
   263  		cmp, err := aDeferredCauset.CompareCauset(sc, &bDeferredCauset)
   264  		if err != nil {
   265  			return 0, err
   266  		}
   267  		if cmp != 0 {
   268  			return cmp, nil
   269  		}
   270  	}
   271  	return us.belowHandleDefCauss.Compare(a, b)
   272  }