// isSameRoute returns true if the join constraint makes the routes // mergeable by unique vindex. The constraint has to be an equality // like a.id = b.id where both columns have the same unique vindex. func (rb *route) isSameRoute(rhs *route, filter sqlparser.BoolExpr) bool { comparison, ok := filter.(*sqlparser.ComparisonExpr) if !ok { return false } if comparison.Operator != sqlparser.EqualStr { return false } left := comparison.Left right := comparison.Right lVindex := rb.Symtab().Vindex(left, rb, false) if lVindex == nil { left, right = right, left lVindex = rb.Symtab().Vindex(left, rb, false) } if lVindex == nil || !vindexes.IsUnique(lVindex) { return false } rVindex := rhs.Symtab().Vindex(right, rhs, false) if rVindex == nil { return false } if rVindex != lVindex { return false } return true }
// getDMLRouting updates the route with the necessary routing // info. If it cannot find a unique route, then it returns an error. func getDMLRouting(where *sqlparser.Where, route *engine.Route) error { if where == nil { return errors.New("unsupported: multi-shard where clause in DML") } for _, index := range route.Table.Ordered { if !vindexes.IsUnique(index.Vindex) { continue } if values := getMatch(where.Expr, index.Column); values != nil { route.Vindex = index.Vindex route.Values = values return nil } } return errors.New("unsupported: multi-shard where clause in DML") }
// computeEqualPlan computes the plan for an equality constraint. func (rb *route) computeEqualPlan(comparison *sqlparser.ComparisonExpr) (opcode engine.RouteOpcode, vindex vindexes.Vindex, values interface{}) { left := comparison.Left right := comparison.Right vindex = rb.Symtab().Vindex(left, rb, true) if vindex == nil { left, right = right, left vindex = rb.Symtab().Vindex(left, rb, true) if vindex == nil { return engine.SelectScatter, nil, nil } } if !exprIsValue(right, rb) { return engine.SelectScatter, nil, nil } if vindexes.IsUnique(vindex) { return engine.SelectEqualUnique, vindex, right } return engine.SelectEqual, vindex, right }
// pushGroupBy processes the group by clause. It resolves all symbols, // and ensures that there are no subqueries. It also verifies that the // references don't addres an outer query. We only support group by // for unsharded or single shard routes. func pushGroupBy(groupBy sqlparser.GroupBy, bldr builder) error { if groupBy == nil { return nil } rb, ok := bldr.(*route) if !ok { return errors.New("unsupported: complex join and group by") } err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { switch node := node.(type) { case *sqlparser.ColName: _, isLocal, err := bldr.Symtab().Find(node, true) if err != nil { return false, err } if !isLocal { return false, errors.New("unsupported: subquery references outer query in group by") } case *sqlparser.Subquery: // TODO(sougou): better error. return false, errors.New("unsupported: subqueries in group by expression") } return true, nil }, groupBy) if err != nil { return err } if rb.IsSingle() { rb.SetGroupBy(groupBy) return nil } // It's a scatter route. We can allow group by if it references a // column with a unique vindex. for _, expr := range groupBy { vindex := bldr.Symtab().Vindex(expr, rb, true) if vindex != nil && vindexes.IsUnique(vindex) { rb.SetGroupBy(groupBy) return nil } } return errors.New("unsupported: scatter and group by") }
// checkAggregates returns an error if the select statement // has aggregates that cannot be pushed down due to a complex // plan. func checkAggregates(sel *sqlparser.Select, bldr builder) error { hasAggregates := false if sel.Distinct != "" { hasAggregates = true } else { _ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { switch node := node.(type) { case *sqlparser.FuncExpr: if node.IsAggregate() { hasAggregates = true return false, errors.New("dummy") } } return true, nil }, sel.SelectExprs) } if !hasAggregates { return nil } // Check if we can allow aggregates. rb, ok := bldr.(*route) if !ok { return errors.New("unsupported: complex join with aggregates") } if rb.IsSingle() { return nil } // It's a scatter rb. We can allow aggregates if there is a unique // vindex in the select list. for _, selectExpr := range sel.SelectExprs { switch selectExpr := selectExpr.(type) { case *sqlparser.NonStarExpr: vindex := bldr.Symtab().Vindex(selectExpr.Expr, rb, true) if vindex != nil && vindexes.IsUnique(vindex) { return nil } } } return errors.New("unsupported: scatter with aggregates") }