func (v *havingVisitor) checkIdentInSelectList(i *expression.Ident) (*expression.Ident, bool, error) { index, err := checkIdentAmbiguous(i, v.selectList, HavingClause) if err != nil { return i, false, errors.Trace(err) } if index >= 0 { // identifier references a select field. use it directly. // e,g. select c1 as c2 from t having c2, here c2 references c1. i.ReferScope = expression.IdentReferSelectList i.ReferIndex = index return i, true, nil } lastIndex := -1 var lastFieldName string // we may meet this select c1 as c2 from t having c1, so we must check origin field name too. for index := 0; index < v.selectList.HiddenFieldOffset; index++ { e := castIdent(v.selectList.Fields[index].Expr) if e == nil { // not identifier continue } if !field.CheckFieldsEqual(e.L, i.L) { // not same, continue continue } if field.IsQualifiedName(i.L) { // qualified name, no need check i.ReferScope = expression.IdentReferSelectList i.ReferIndex = index return i, true, nil } if lastIndex == -1 { lastIndex = index lastFieldName = e.L continue } // we may meet select t1.c as a, t2.c as b from t1, t2 having c, must check ambiguous here if !field.CheckFieldsEqual(lastFieldName, e.L) { return i, false, errors.Errorf("Column '%s' in having clause is ambiguous", i) } i.ReferScope = expression.IdentReferSelectList i.ReferIndex = index return i, true, nil } if lastIndex != -1 { i.ReferScope = expression.IdentReferSelectList i.ReferIndex = lastIndex return i, true, nil } return i, false, nil }
func (*testResultFieldSuite) TestCheckFieldEquals(c *C) { x := "a.b.c" y := "a.b.c" c.Assert(field.CheckFieldsEqual(x, y), IsTrue) x = "a.b.c" y = "a.B.c" c.Assert(field.CheckFieldsEqual(x, y), IsTrue) x = "b.c" y = "a.b.c" c.Assert(field.CheckFieldsEqual(x, y), IsTrue) x = "a..c" y = "a.b.c" c.Assert(field.CheckFieldsEqual(x, y), IsTrue) x = "a.a..c" y = "a.b.c" c.Assert(field.CheckFieldsEqual(x, y), IsTrue) x = "a.b.d" y = "a.b.c" c.Assert(field.CheckFieldsEqual(x, y), IsFalse) x = "a.d.c" y = "a.b.c" c.Assert(field.CheckFieldsEqual(x, y), IsFalse) x = "d.b.c" y = "a.b.c" c.Assert(field.CheckFieldsEqual(x, y), IsFalse) }
func (v *havingVisitor) checkIdentInGroupBy(i *expression.Ident) (*expression.Ident, bool, error) { for _, by := range v.groupBy { e := castIdent(by) if e == nil { // group by must be a identifier too continue } if !field.CheckFieldsEqual(e.L, i.L) { // not same, continue continue } // if group by references select list or having is qualified identifier, // no other check. if e.ReferScope == expression.IdentReferSelectList || field.IsQualifiedName(i.L) { i.ReferScope = e.ReferScope i.ReferIndex = e.ReferIndex return i, true, nil } // having is unqualified name, e.g, select * from t1, t2 group by t1.c having c. // both t1 and t2 have column c, we must check ambiguous here. idx := field.GetResultFieldIndex(i.L, v.selectList.FromFields) if len(idx) > 1 { return i, false, errors.Errorf("Column '%s' in having clause is ambiguous", i) } i.ReferScope = e.ReferScope i.ReferIndex = e.ReferIndex return i, true, nil } return i, false, nil }
// CheckAndUpdateSelectList checks having fields validity and set hidden fields to selectList. func (r *HavingRset) CheckAndUpdateSelectList(selectList *plans.SelectList, groupBy []expression.Expression, tableFields []*field.ResultField) error { if err := expressions.CheckOneColumn(r.Expr); err != nil { return errors.Trace(err) } if expressions.ContainAggregateFunc(r.Expr) { expr, err := selectList.UpdateAggFields(r.Expr, tableFields) if err != nil { return errors.Errorf("%s in 'having clause'", err.Error()) } r.Expr = expr } else { // having can only contain group by column and select list, e.g, // `select c1 from t group by c2 having c3 > 0` is invalid, // because c3 is not in group by and select list. names := expressions.MentionedColumns(r.Expr) for _, name := range names { found := false // check name whether in select list. // notice that `select c1 as c2 from t group by c1, c2, c3 having c2 > c3`, // will use t.c2 not t.c1 here. if field.ContainFieldName(name, selectList.ResultFields, field.OrgFieldNameFlag) { continue } if field.ContainFieldName(name, selectList.ResultFields, field.FieldNameFlag) { if field.ContainFieldName(name, tableFields, field.OrgFieldNameFlag) { selectList.CloneHiddenField(name, tableFields) } continue } // check name whether in group by. // group by must only have column name, e.g, // `select c1 from t group by c2 having c2 > 0` is valid, // but `select c1 from t group by c2 + 1 having c2 > 0` is invalid. for _, by := range groupBy { if !field.CheckFieldsEqual(name, by.String()) { continue } // if name is not in table fields, it will get an unknown field error in GroupByRset, // so no need to check return value. selectList.CloneHiddenField(name, tableFields) found = true break } if !found { return errors.Errorf("Unknown column '%s' in 'having clause'", name) } } } return nil }