code: func (e *IndexLookUpJoin) Open(ctx context.Context) error {
  	// Be careful, very dirty hack in this line!!!
  	// IndexLookUpJoin need to rebuild executor (the dataReaderBuilder) during
  	// executing. However `executor.Next()` is lazy evaluation when the RecordSet
  	// result is drained.
  	// Lazy evaluation means the saved session context may change during executor's
  	// building and its running.
  	// A specific sequence for example:
  	//
  	// e := buildExecutor()   // txn at build time
  	// recordSet := runStmt(e)
  	// session.CommitTxn()    // txn closed
  	// recordSet.Next()
  	// e.dataReaderBuilder.Build() // txn is used again, which is already closed
  	//
  	// The trick here is `getStartTS` will cache start ts in the dataReaderBuilder,
  	// so even txn is destroyed later, the dataReaderBuilder could still use the
  	// cached start ts to construct DAG.
  	_, err := e.innerCtx.readerBuilder.getStartTS()
  	if err != nil {
  		return err
  	}
  
  	err = e.children[0].Open(ctx)
  	if err != nil {
  		return err
  	}
  	e.memTracker = memory.NewTracker(e.id, e.ctx.GetSessionVars().MemQuotaIndexLookupJoin)
  	e.memTracker.AttachTo(e.ctx.GetSessionVars().StmtCtx.MemTracker)
  	e.innerPtrBytes = make([][]byte, 0, 8)
  	e.startWorkers(ctx)
  	return nil
  }
docs: // Open implements the Executor interface.