code: func (p *Join) convert2PhysicalPlan(prop *requiredProperty) (*physicalPlanInfo, error) {
  	info, err := p.getPlanInfo(prop)
  	if err != nil {
  		return nil, errors.Trace(err)
  	}
  	if info != nil {
  		return info, nil
  	}
  	switch p.JoinType {
  	case SemiJoin, LeftOuterSemiJoin:
  		info, err = p.convert2PhysicalPlanSemi(prop)
  		if err != nil {
  			return nil, errors.Trace(err)
  		}
  	case LeftOuterJoin:
  		info, err = p.convert2PhysicalPlanLeft(prop, false)
  		if err != nil {
  			return nil, errors.Trace(err)
  		}
  	case RightOuterJoin:
  		info, err = p.convert2PhysicalPlanRight(prop, false)
  		if err != nil {
  			return nil, errors.Trace(err)
  		}
  	default:
  		lInfo, err := p.convert2PhysicalPlanLeft(prop, true)
  		if err != nil {
  			return nil, errors.Trace(err)
  		}
  		rInfo, err := p.convert2PhysicalPlanRight(prop, true)
  		if err != nil {
  			return nil, errors.Trace(err)
  		}
  		if rInfo.cost < lInfo.cost {
  			info = rInfo
  		} else {
  			info = lInfo
  		}
  	}
  	p.storePlanInfo(prop, info)
  	return info, nil
  }
docs: // convert2PhysicalPlan implements the LogicalPlan convert2PhysicalPlan interface.