Exemplo n.º 1
0
// 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
}
Exemplo n.º 2
0
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)
	}
}