code: func (b *PlanBuilder) buildWindowFunctionFrame(spec *ast.WindowSpec, orderByItems []property.Item) (*WindowFrame, error) {
  	frameClause := spec.Frame
  	if frameClause == nil {
  		return nil, nil
  	}
  	if frameClause.Type == ast.Groups {
  		return nil, ErrNotSupportedYet.GenWithStackByArgs("GROUPS")
  	}
  	frame := &WindowFrame{Type: frameClause.Type}
  	start := frameClause.Extent.Start
  	if start.Type == ast.Following && start.UnBounded {
  		return nil, ErrWindowFrameStartIllegal.GenWithStackByArgs(getWindowName(spec.Name.O))
  	}
  	var err error
  	frame.Start, err = b.buildWindowFunctionFrameBound(spec, orderByItems, &start)
  	if err != nil {
  		return nil, err
  	}
  
  	end := frameClause.Extent.End
  	if end.Type == ast.Preceding && end.UnBounded {
  		return nil, ErrWindowFrameEndIllegal.GenWithStackByArgs(getWindowName(spec.Name.O))
  	}
  	frame.End, err = b.buildWindowFunctionFrameBound(spec, orderByItems, &end)
  	return frame, err
  }
docs: // buildWindowFunctionFrame builds the window function frames.
  // See https://dev.mysql.com/doc/refman/8.0/en/window-functions-frames.html