code: func finishCopTask(ctx sessionctx.Context, task task) task {
  	t, ok := task.(*copTask)
  	if !ok {
  		return task
  	}
  	// FIXME: When it is a double reading. The cost should be more expensive. The right cost should add the
  	// `NetWorkStartCost` * (totalCount / perCountIndexRead)
  	t.finishIndexPlan()
  	if t.tablePlan != nil {
  		t.cst += t.count() * netWorkFactor
  	}
  	newTask := &rootTask{
  		cst: t.cst,
  	}
  	if t.indexPlan != nil && t.tablePlan != nil {
  		p := PhysicalIndexLookUpReader{tablePlan: t.tablePlan, indexPlan: t.indexPlan}.Init(ctx)
  		p.stats = t.tablePlan.statsInfo()
  		if t.doubleReadNeedProj {
  			schema := p.IndexPlans[0].(*PhysicalIndexScan).dataSourceSchema
  			proj := PhysicalProjection{Exprs: expression.Column2Exprs(schema.Columns)}.Init(ctx, p.stats, nil)
  			proj.SetSchema(schema)
  			proj.SetChildren(p)
  			newTask.p = proj
  		} else {
  			newTask.p = p
  		}
  	} else if t.indexPlan != nil {
  		p := PhysicalIndexReader{indexPlan: t.indexPlan}.Init(ctx)
  		p.stats = t.indexPlan.statsInfo()
  		newTask.p = p
  	} else {
  		p := PhysicalTableReader{tablePlan: t.tablePlan}.Init(ctx)
  		p.stats = t.tablePlan.statsInfo()
  		newTask.p = p
  	}
  	return newTask
  }
docs: // finishCopTask means we close the coprocessor task and create a root task.