code: func (f *projectionInputFetcher) run(ctx context.Context) {
  	var output *projectionOutput
  	defer func() {
  		if r := recover(); r != nil {
  			recoveryProjection(output, r)
  		}
  		close(f.globalOutputCh)
  	}()
  
  	for {
  		input := readProjectionInput(f.inputCh, f.globalFinishCh)
  		if input == nil {
  			return
  		}
  		targetWorker := input.targetWorker
  
  		output = readProjectionOutput(f.outputCh, f.globalFinishCh)
  		if output == nil {
  			return
  		}
  
  		f.globalOutputCh <- output
  
  		requiredRows := atomic.LoadInt64(&f.proj.parentReqRows)
  		input.chk.SetRequiredRows(int(requiredRows), f.proj.maxChunkSize)
  		err := f.child.Next(ctx, chunk.NewRecordBatch(input.chk))
  		if err != nil || input.chk.NumRows() == 0 {
  			output.done <- err
  			return
  		}
  
  		targetWorker.inputCh <- input
  		targetWorker.outputCh <- output
  	}
  }
docs: // run gets projectionInputFetcher's input and output resources from its
  // "inputCh" and "outputCh" channel, once the input and output resources are
  // abtained, it fetches child's result into "input.chk" and:
  //   a. Dispatches this input to the worker specified in "input.targetWorker"
  //   b. Dispatches this output to the main thread: "ProjectionExec.Next"
  //   c. Dispatches this output to the worker specified in "input.targetWorker"
  //
  // It is finished and exited once:
  //   a. There is no more input from child.
  //   b. "ProjectionExec" close the "globalFinishCh"