code: func (p *Apply) convert2PhysicalPlan(prop *requiredProperty) (*physicalPlanInfo, error) {
  	info, err := p.getPlanInfo(prop)
  	if err != nil {
  		return info, errors.Trace(err)
  	}
  	if info != nil {
  		return info, nil
  	}
  	if p.JoinType == InnerJoin {
  		info, err = p.Join.convert2PhysicalPlanLeft(prop, true)
  	} else {
  		info, err = p.Join.convert2PhysicalPlanSemi(prop)
  	}
  	if err != nil {
  		return info, errors.Trace(err)
  	}
  	switch info.p.(type) {
  	case *PhysicalHashJoin, *PhysicalHashSemiJoin:
  		ap := &PhysicalApply{
  			PhysicalJoin: info.p,
  			OuterSchema:  p.corCols,
  		}
  		ap.tp = App
  		ap.allocator = p.allocator
  		ap.initIDAndContext(p.ctx)
  		ap.SetChildren(info.p.GetChildren()...)
  		ap.SetSchema(info.p.GetSchema())
  		info.p = ap
  	default:
  		info.cost = math.MaxFloat64
  		info.p = nil
  	}
  	p.storePlanInfo(prop, info)
  	return info, nil
  }
docs: // convert2PhysicalPlan implements the LogicalPlan convert2PhysicalPlan interface.