// Plan implements the plan.Planner interface. // The whole phase for select is // `from -> where -> lock -> group by -> having -> select fields -> distinct -> order by -> limit -> final` func (s *SelectStmt) Plan(ctx context.Context) (plan.Plan, error) { var ( r plan.Plan err error ) if s.From != nil { r, err = s.From.Plan(ctx) if err != nil { return nil, err } } else if s.Fields != nil { // Only evaluate fields values. fr := &rsets.SelectFromDualRset{Fields: s.Fields} r, err = fr.Plan(ctx) if err != nil { return nil, err } } // Put RowStackFromPlan here so that we can catch the origin from data after above FROM phase. r = &plans.RowStackFromPlan{Src: r} if w := s.Where; w != nil { r, err = (&rsets.WhereRset{Expr: w.Expr, Src: r}).Plan(ctx) if err != nil { return nil, err } } lock := s.Lock if variable.ShouldAutocommit(ctx) { // Locking of rows for update using SELECT FOR UPDATE only applies when autocommit // is disabled (either by beginning transaction with START TRANSACTION or by setting // autocommit to 0. If autocommit is enabled, the rows matching the specification are not locked. // See: https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html lock = coldef.SelectLockNone } r = &plans.SelectLockPlan{Src: r, Lock: lock} if err := s.checkOneColumn(ctx); err != nil { return nil, errors.Trace(err) } // Get select list for futher field values evaluation. selectList, err := plans.ResolveSelectList(s.Fields, r.GetFields()) if err != nil { return nil, errors.Trace(err) } var groupBy []expression.Expression if s.GroupBy != nil { groupBy = s.GroupBy.By } if s.Having != nil { // `having` may contain aggregate functions, and we will add this to hidden fields. if err = s.Having.CheckAndUpdateSelectList(selectList, groupBy, r.GetFields()); err != nil { return nil, errors.Trace(err) } } if s.OrderBy != nil { // `order by` may contain aggregate functions, and we will add this to hidden fields. if err = s.OrderBy.CheckAndUpdateSelectList(selectList, r.GetFields()); err != nil { return nil, errors.Trace(err) } } switch { case !rsets.HasAggFields(selectList.Fields) && s.GroupBy == nil: // If no group by and no aggregate functions, we will use SelectFieldsPlan. if r, err = (&rsets.SelectFieldsRset{Src: r, SelectList: selectList, }).Plan(ctx); err != nil { return nil, err } default: if r, err = (&rsets.GroupByRset{By: groupBy, Src: r, SelectList: selectList, }).Plan(ctx); err != nil { return nil, err } } if s := s.Having; s != nil { if r, err = (&rsets.HavingRset{ Src: r, Expr: s.Expr}).Plan(ctx); err != nil { return nil, err } } if s.Distinct { r = &plans.DistinctDefaultPlan{Src: r, SelectList: selectList} } if s := s.OrderBy; s != nil { if r, err = (&rsets.OrderByRset{By: s.By, Src: r, SelectList: selectList, }).Plan(ctx); err != nil { return nil, err } } if s := s.Offset; s != nil { r = &plans.OffsetDefaultPlan{Count: s.Count, Src: r, Fields: r.GetFields()} } if s := s.Limit; s != nil { r = &plans.LimitDefaultPlan{Count: s.Count, Src: r, Fields: r.GetFields()} } r = &plans.SelectFinalPlan{Src: r, SelectList: selectList} return r, nil }
func (s *testSelectListSuite) TestAmbiguous(c *C) { type pair struct { Name string AsName string } createResultFields := func(names []string) []*field.ResultField { fs := make([]*field.ResultField, 0, len(names)) for i := 0; i < len(names); i++ { f := &field.ResultField{ TableName: "t", Name: names[i], } f.ColumnInfo.Name = model.NewCIStr(names[i]) fs = append(fs, f) } return fs } createFields := func(ps []pair) []*field.Field { fields := make([]*field.Field, len(ps)) for i, f := range ps { fields[i] = &field.Field{ Expr: &expression.Ident{ CIStr: model.NewCIStr(f.Name), }, AsName: f.AsName, } } return fields } tbl := []struct { Fields []pair Name string Err bool Index int }{ {[]pair{{"id", ""}}, "id", false, 0}, {[]pair{{"id", "a"}, {"name", "a"}}, "a", true, -1}, {[]pair{{"id", "a"}, {"name", "a"}}, "id", false, -1}, } for _, t := range tbl { rs := createResultFields([]string{"id", "name"}) fs := createFields(t.Fields) sl, err := plans.ResolveSelectList(fs, rs) c.Assert(err, IsNil) index, err := sl.CheckReferAmbiguous(&expression.Ident{ CIStr: model.NewCIStr(t.Name), }) if t.Err { c.Assert(err, NotNil) continue } c.Assert(err, IsNil) c.Assert(t.Index, Equals, index) } }