code: func (s *joinReOrderSolver) optimizeRecursive(ctx sessionctx.Context, p LogicalPlan) (LogicalPlan, error) {
  	var err error
  	curJoinGroup, eqEdges, otherConds := extractJoinGroup(p)
  	if len(curJoinGroup) > 1 {
  		for i := range curJoinGroup {
  			curJoinGroup[i], err = s.optimizeRecursive(ctx, curJoinGroup[i])
  			if err != nil {
  				return nil, err
  			}
  		}
  		baseGroupSolver := &baseSingleGroupJoinOrderSolver{
  			ctx:        ctx,
  			otherConds: otherConds,
  		}
  		if len(curJoinGroup) > ctx.GetSessionVars().TiDBOptJoinReorderThreshold {
  			groupSolver := &joinReorderGreedySolver{
  				baseSingleGroupJoinOrderSolver: baseGroupSolver,
  				eqEdges:                        eqEdges,
  			}
  			p, err = groupSolver.solve(curJoinGroup)
  		} else {
  			dpSolver := &joinReorderDPSolver{
  				baseSingleGroupJoinOrderSolver: baseGroupSolver,
  			}
  			dpSolver.newJoin = dpSolver.newJoinWithEdges
  			p, err = dpSolver.solve(curJoinGroup, expression.ScalarFuncs2Exprs(eqEdges))
  		}
  		if err != nil {
  			return nil, err
  		}
  		return p, nil
  	}
  	newChildren := make([]LogicalPlan, 0, len(p.Children()))
  	for _, child := range p.Children() {
  		newChild, err := s.optimizeRecursive(ctx, child)
  		if err != nil {
  			return nil, err
  		}
  		newChildren = append(newChildren, newChild)
  	}
  	p.SetChildren(newChildren...)
  	return p, nil
  }
docs: // optimizeRecursive recursively collects join groups and applies join reorder algorithm for each group.