code: func Optimize(ctx context.Context, node ast.Node, is infoschema.InfoSchema) (Plan, error) {
  	// We have to infer type again because after parameter is set, the expression type may change.
  	if err := InferType(ctx.GetSessionVars().StmtCtx, node); err != nil {
  		return nil, errors.Trace(err)
  	}
  	allocator := new(idAllocator)
  	builder := &planBuilder{
  		ctx:       ctx,
  		is:        is,
  		colMapper: make(map[*ast.ColumnNameExpr]int),
  		allocator: allocator}
  	p := builder.build(node)
  	if builder.err != nil {
  		return nil, errors.Trace(builder.err)
  	}
  	if logic, ok := p.(LogicalPlan); ok {
  		return doOptimize(logic, ctx, allocator)
  	}
  	return p, nil
  }
docs: // Optimize does optimization and creates a Plan.
  // The node must be prepared first.