code: func (e *IndexLookUpJoin) Next(ctx context.Context, req *chunk.RecordBatch) error {
  	if e.runtimeStats != nil {
  		start := time.Now()
  		defer func() { e.runtimeStats.Record(time.Since(start), req.NumRows()) }()
  	}
  	if e.isOuterJoin {
  		atomic.StoreInt64(&e.requiredRows, int64(req.RequiredRows()))
  	}
  	req.Reset()
  	e.joinResult.Reset()
  	for {
  		task, err := e.getFinishedTask(ctx)
  		if err != nil {
  			return err
  		}
  		if task == nil {
  			return nil
  		}
  		if e.innerIter == nil || e.innerIter.Current() == e.innerIter.End() {
  			e.lookUpMatchedInners(task, task.cursor)
  			e.innerIter = chunk.NewIterator4Slice(task.matchedInners)
  			e.innerIter.Begin()
  		}
  
  		outerRow := task.outerResult.GetRow(task.cursor)
  		if e.innerIter.Current() != e.innerIter.End() {
  			matched, isNull, err := e.joiner.tryToMatch(outerRow, e.innerIter, req.Chunk)
  			if err != nil {
  				return err
  			}
  			task.hasMatch = task.hasMatch || matched
  			task.hasNull = task.hasNull || isNull
  		}
  		if e.innerIter.Current() == e.innerIter.End() {
  			if !task.hasMatch {
  				e.joiner.onMissMatch(task.hasNull, outerRow, req.Chunk)
  			}
  			task.cursor++
  			task.hasMatch = false
  			task.hasNull = false
  		}
  		if req.IsFull() {
  			return nil
  		}
  	}
  }
docs: // Next implements the Executor interface.