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 }
func (v *havingVisitor) visitIdentInAggregate(i *expression.Ident) (expression.Expression, error) { // if we are visiting aggregate function arguments, the identifier first checks in from table, // then in select list, and outer query finally. // find this identifier in FROM. idx := field.GetResultFieldIndex(i.L, v.selectList.FromFields) if len(idx) > 0 { i.ReferScope = expression.IdentReferFromTable i.ReferIndex = idx[0] return i, nil } // check in select list. index, err := checkIdentAmbiguous(i, v.selectList, HavingClause) if err != nil { return i, errors.Trace(err) } if index >= 0 { // find in select list i.ReferScope = expression.IdentReferSelectList i.ReferIndex = index return i, nil } // TODO: check in out query // TODO: return unknown field error, but now just return directly. // Because this may reference outer query. return i, nil }
func (v *groupByVisitor) VisitIdent(i *expression.Ident) (expression.Expression, error) { // Group by ambiguous rule: // select c1 as a, c2 as a from t group by a is ambiguous // select c1 as a, c2 as a from t group by a + 1 is ambiguous // select c1 as c2, c2 from t group by c2 is ambiguous // select c1 as c2, c2 from t group by c2 + 1 is not ambiguous var ( index int err error ) if v.rootIdent == i { // The group by is an identifier, we must check it first. index, err = checkIdentAmbiguous(i, v.selectList, GroupByClause) if err != nil { return nil, errors.Trace(err) } } // first find this identifier in FROM. idx := field.GetResultFieldIndex(i.L, v.selectList.FromFields) if len(idx) > 0 { i.ReferScope = expression.IdentReferFromTable i.ReferIndex = idx[0] return i, nil } if v.rootIdent != i { // This identifier is the part of the group by, check ambiguous here. index, err = checkIdentAmbiguous(i, v.selectList, GroupByClause) if err != nil { return nil, errors.Trace(err) } } // try to find in select list, we have got index using checkIdent before. if index >= 0 { // group by can not reference aggregate fields if _, ok := v.selectList.AggFields[index]; ok { return nil, errors.Errorf("Reference '%s' not supported (reference to group function)", i) } // find in select list i.ReferScope = expression.IdentReferSelectList i.ReferIndex = index return i, nil } // TODO: check in out query return i, errors.Errorf("Unknown column '%s' in 'group statement'", i) }
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 *fromIdentVisitor) VisitIdent(i *expression.Ident) (expression.Expression, error) { idx := field.GetResultFieldIndex(i.L, v.fromFields, field.DefaultFieldFlag) if len(idx) == 1 { i.ReferScope = expression.IdentReferFromTable i.ReferIndex = idx[0] return i, nil } else if len(idx) > 1 { return nil, errors.Errorf("Column '%s' in %s is ambiguous", i, v.clause) } if v.clause == onClause { // on clause can't check outer query. return nil, errors.Errorf("Unknown column '%s' in '%s'", i, v.clause) } // TODO: check in outer query return i, nil }
func (v *orderByVisitor) VisitIdent(i *expression.Ident) (expression.Expression, error) { // Order by ambiguous rule: // select c1 as a, c2 as a from t order by a is ambiguous // select c1 as a, c2 as a from t order by a + 1 is ambiguous // select c1 as c2, c2 from t order by c2 is ambiguous // select c1 as c2, c2 from t order by c2 + 1 is not ambiguous // Order by identifier reference check // select c1 as c2 from t order by c2, c2 in order by references c1 in select field. // select c1 as c2 from t order by c2 + 1, c2 in order by c2 + 1 references t.c2 in from table. // select c1 as c2 from t order by sum(c2), c2 in order by sum(c2) references t.c2 in from table. // select c1 as c2 from t order by sum(1) + c2, c2 in order by sum(1) + c2 references t.c2 in from table. // select c1 as a from t order by a, a references c1 in select field. // select c1 as a from t order by sum(a), a in order by sum(a) references c1 in select field. // TODO: unify VisitIdent for group by and order by visitor, they are little different. var ( index int err error ) if v.rootIdent == i { // The order by is an identifier, we must check it first. index, err = checkIdentAmbiguous(i, v.selectList, OrderByClause) if err != nil { return nil, errors.Trace(err) } if index >= 0 { // identifier references a select field. use it directly. // e,g. select c1 as c2 from t order by c2, here c2 references c1. i.ReferScope = expression.IdentReferSelectList i.ReferIndex = index return i, nil } } // find this identifier in FROM. idx := field.GetResultFieldIndex(i.L, v.selectList.FromFields) if len(idx) > 0 { i.ReferScope = expression.IdentReferFromTable i.ReferIndex = idx[0] return i, nil } if v.rootIdent != i { // This identifier is the part of the order by, check ambiguous here. index, err = checkIdentAmbiguous(i, v.selectList, OrderByClause) if err != nil { return nil, errors.Trace(err) } } // try to find in select list, we have got index using checkIdentAmbiguous before. if index >= 0 { // find in select list i.ReferScope = expression.IdentReferSelectList i.ReferIndex = index return i, nil } // TODO: check in out query return i, errors.Errorf("Unknown column '%s' in 'order clause'", i) }