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 (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 }
// CheckAmbiguous checks whether an identifier reference is ambiguous or not in select list. // e,g, "select c1 as a, c2 as a from t group by a" is ambiguous, // but "select c1 as a, c1 as a from t group by a" is not. // "select c1 as a, c2 + 1 as a from t group by a" is not ambiguous too, // If no ambiguous, -1 means expr refers none in select list, else an index for first match. // CheckAmbiguous will break the check when finding first matching which is not an indentifier, // or an index for an identifier field in the end, -1 means none found. func (s *SelectList) CheckAmbiguous(expr expression.Expression) (int, error) { if _, ok := expr.(*expression.Ident); !ok { return -1, nil } name := expr.String() if field.IsQualifiedName(name) { // name is qualified, no need to check return -1, nil } // select c1 as a, 1 as a, c2 as a from t order by a is not ambiguous. // select c1 as a, c2 as a from t order by a is ambiguous. // select 1 as a, c1 as a from t order by a is not ambiguous. // select c1 as a, sum(c1) as a from t group by a is error. // select c1 as a, 1 as a, sum(c1) as a from t group by a is not error. // so we will break the check if matching a none identifier field. lastIndex := -1 // only check origin select list, no hidden field. for i := 0; i < s.HiddenFieldOffset; i++ { if !strings.EqualFold(s.ResultFields[i].Name, name) { continue } if _, ok := s.Fields[i].Expr.(*expression.Ident); !ok { // not identfier, return directly. return i, nil } if lastIndex == -1 { // first match, continue lastIndex = i continue } // check origin name, e,g. "select c1 as c2, c2 from t group by c2" is ambiguous. if s.ResultFields[i].ColumnInfo.Name.L != s.ResultFields[lastIndex].ColumnInfo.Name.L { return -1, errors.Errorf("refer %s is ambiguous", expr) } // check table name, e.g, "select t.c1, c1 from t group by c1" is not ambiguous. if s.ResultFields[i].TableName != s.ResultFields[lastIndex].TableName { return -1, errors.Errorf("refer %s is ambiguous", expr) } // TODO: check database name if possible. } return lastIndex, nil }