Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gen4: planning Select IN #8155

Merged
merged 2 commits into from
May 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions go/vt/vtgate/planbuilder/jointree_transformers.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ func transformRoutePlan(n *routePlan) (*route, error) {
tableNameMap[sqlparser.String(t.qtable.table.Name)] = nil
}

for _, predicate := range n.vindexPredicates {
switch predicate := predicate.(type) {
case *sqlparser.ComparisonExpr:
if predicate.Operator == sqlparser.InOp {
predicate.Right = sqlparser.ListArg(engine.ListVarName)
}
}
}

predicates := n.Predicates()
var where *sqlparser.Where
if predicates != nil {
Expand Down
112 changes: 80 additions & 32 deletions go/vt/vtgate/planbuilder/route_planning.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ func newBuildSelectPlan(sel *sqlparser.Select, vschema ContextVSchema) (engine.P
if err != nil {
return nil, err
}
if len(qgraph.subqueries) > 0 {
return nil, semantics.Gen4NotSupportedF("subquery")
}

var tree joinTree

Expand Down Expand Up @@ -192,8 +195,9 @@ type (
predicates []sqlparser.Expr

// vindex and vindexValues is set if a vindex will be used for this route.
vindex vindexes.Vindex
vindexValues []sqltypes.PlanValue
vindex vindexes.Vindex
vindexValues []sqltypes.PlanValue
vindexPredicates []sqlparser.Expr

// here we store the possible vindexes we can use so that when we add predicates to the plan,
// we can quickly check if the new predicates enables any new vindex options
Expand Down Expand Up @@ -265,7 +269,9 @@ type vindexPlusPredicates struct {
vindex *vindexes.ColumnVindex
values []sqltypes.PlanValue
// Vindex is covered if all the columns in the vindex have an associated predicate
covered bool
covered bool
opcode engine.RouteOpcode
predicates []sqlparser.Expr
}

// addPredicate clones this routePlan and returns a new one with these predicates added to it. if the predicates can help,
Expand All @@ -292,49 +298,96 @@ func (rp *routePlan) searchForNewVindexes(predicates []sqlparser.Expr) (bool, er
for _, filter := range predicates {
switch node := filter.(type) {
case *sqlparser.ComparisonExpr:
if sqlparser.IsNull(node.Left) || sqlparser.IsNull(node.Right) {
// we are looking at ANDed predicates in the WHERE clause.
// since we know that nothing returns true when compared to NULL,
// so we can safely bail out here
rp.routeOpCode = engine.SelectNone
return false, nil
}

switch node.Operator {
case sqlparser.EqualOp:
// here we are searching for predicates in the form n.col = XYZ
if sqlparser.IsNull(node.Left) || sqlparser.IsNull(node.Right) {
// we are looking at ANDed predicates in the WHERE clause.
// since we know that nothing returns true when compared to NULL,
// so we can safely bail out here
rp.routeOpCode = engine.SelectNone
return false, nil
column, ok := node.Left.(*sqlparser.ColName)
other := node.Right
if !ok {
column, ok = node.Right.(*sqlparser.ColName)
if !ok {
// either the LHS or RHS have to be a column to be useful for the vindex
continue
}
other = node.Left
}
value, err := sqlparser.NewPlanValue(other)
if err != nil {
// if we are unable to create a PlanValue, we can't use a vindex, but we don't have to fail
if strings.Contains(err.Error(), "expression is too complex") {
continue
}
// something else went wrong, return the error
return false, err
}
// here we are searching for predicates in the form n.col = XYZ
// TODO(Manan,Andres): Remove the predicates that are repeated eg. Id=1 AND Id=1
for _, v := range rp.vindexPreds {
if v.covered {
// already covered by an earlier predicate
continue
}
column, ok := node.Left.(*sqlparser.ColName)
other := node.Right
if !ok {
column, ok = node.Right.(*sqlparser.ColName)
other = node.Left
}
value, err := sqlparser.NewPlanValue(other)
if err != nil {
// if we are unable to create a PlanValue, we can't use a vindex, but we don't have to fail
if strings.Contains(err.Error(), "expression is too complex") {
continue
}
// something else went wrong, return the error
return false, err
}
if ok {
for _, col := range v.vindex.Columns {
// If the column for the predicate matches any column in the vindex add it to the list
if column.Name.Equal(col) {
v.values = append(v.values, value)
v.predicates = append(v.predicates, node)
// Vindex is covered if all the columns in the vindex have a associated predicate
v.covered = len(v.values) == len(v.vindex.Columns)
v.opcode = engine.SelectEqual
if v.vindex.Vindex.IsUnique() {
v.opcode = engine.SelectEqualUnique
}
newVindexFound = newVindexFound || v.covered
}
}
}
}
case sqlparser.InOp:
column, ok := node.Left.(*sqlparser.ColName)
if !ok {
continue
}
value, err := sqlparser.NewPlanValue(node.Right)
if err != nil {
// if we are unable to create a PlanValue, we can't use a vindex, but we don't have to fail
if strings.Contains(err.Error(), "expression is too complex") {
continue
}
// something else went wrong, return the error
return false, err
}
switch nodeR := node.Right.(type) {
case sqlparser.ValTuple:
if len(nodeR) == 1 && sqlparser.IsNull(nodeR[0]) {
rp.routeOpCode = engine.SelectNone
return false, nil
}
}
for _, v := range rp.vindexPreds {
if v.covered {
continue
}
for _, col := range v.vindex.Columns {
// If the column for the predicate matches any column in the vindex add it to the list
if column.Name.Equal(col) {
v.values = append(v.values, value)
v.predicates = append(v.predicates, node)
// Vindex is covered if all the columns in the vindex have a associated predicate
v.covered = len(v.values) == len(v.vindex.Columns)
v.opcode = engine.SelectIN
newVindexFound = newVindexFound || v.covered
}
}
}
default:
return false, semantics.Gen4NotSupportedF("%s", sqlparser.String(filter))
}
Expand All @@ -351,15 +404,10 @@ func (rp *routePlan) pickBestAvailableVindex() {
}
// Choose the minimum cost vindex from the ones which are covered
if rp.vindex == nil || v.vindex.Vindex.Cost() < rp.vindex.Cost() {
rp.routeOpCode = v.opcode
rp.vindex = v.vindex.Vindex
rp.vindexValues = v.values
}
}

if rp.vindex != nil {
rp.routeOpCode = engine.SelectEqual
if rp.vindex.IsUnique() {
rp.routeOpCode = engine.SelectEqualUnique
rp.vindexPredicates = v.predicates
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions go/vt/vtgate/planbuilder/testdata/filter_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ Gen4 plan same as above
"Vindex": "name_user_map"
}
}
Gen4 plan same as above

# Composite IN clause
"select id from user where (name, col) in (('aa', 'bb'), ('cc', 'dd'))"
Expand Down Expand Up @@ -294,6 +295,7 @@ Gen4 plan same as above
"Vindex": "user_index"
}
}
Gen4 plan same as above

# Composite IN: multiple vindex matches
"select id from user where (costly, name) in (('aa', 'bb'), ('cc', 'dd'))"
Expand Down Expand Up @@ -387,6 +389,7 @@ Gen4 plan same as above
"Table": "`user`"
}
}
Gen4 plan same as above

# Composite IN: RHS not tuple
"select id from user where (col1, name) in (select * from music where music.user_id=user.id)"
Expand Down Expand Up @@ -423,6 +426,7 @@ Gen4 plan same as above
"Table": "`user`"
}
}
Gen4 plan same as above

# IN clause: LHS is neither column nor composite tuple
"select Id from user where 1 in ('aa', 'bb')"
Expand Down Expand Up @@ -459,6 +463,7 @@ Gen4 plan same as above
"Table": "`user`"
}
}
Gen4 plan same as above

# Single table equality route with val arg
"select id from user where name = :a"
Expand Down Expand Up @@ -527,6 +532,7 @@ Gen4 plan same as above
"Vindex": "name_user_map"
}
}
Gen4 plan same as above

# Multi-table unique vindex constraint
"select user_extra.id from user join user_extra on user.id = user_extra.user_id where user.id = 5"
Expand Down Expand Up @@ -805,6 +811,7 @@ Gen4 plan same as above
"Vindex": "user_index"
}
}
Gen4 plan same as above

# Route with multiple route constraints and boolean, SelectIN is the best constraint.
"select id from user where user.col = case user.col when 'foo' then true else false end and user.id in (1, 2)"
Expand All @@ -830,6 +837,7 @@ Gen4 plan same as above
"Vindex": "user_index"
}
}
Gen4 plan same as above

# Route with multiple route constraints and boolean, SelectEqual is the best constraint.
"select (id or col) as val from user where user.col = 5 and user.id in (1, 2) and user.name = 'aa'"
Expand Down Expand Up @@ -918,6 +926,7 @@ Gen4 plan same as above
"Vindex": "user_index"
}
}
Gen4 plan same as above

# Route with OR and AND clause, must parenthesize correctly.
"select id from user where user.id = 1 or user.name = 'aa' and user.id in (1, 2)"
Expand Down Expand Up @@ -1711,6 +1720,7 @@ Gen4 plan same as above
"Table": "music"
}
}
Gen4 plan same as above

# Single table with unique vindex match and IN (null, 1, 2)
"select id from music where user_id = 4 and id IN (null, 1, 2)"
Expand All @@ -1733,6 +1743,7 @@ Gen4 plan same as above
"Vindex": "user_index"
}
}
Gen4 plan same as above

# Single table with unique vindex match and NOT IN (null, 1, 2)
"select id from music where user_id = 4 and id NOT IN (null, 1, 2)"
Expand Down
1 change: 1 addition & 0 deletions go/vt/vtgate/planbuilder/testdata/wireup_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,7 @@
# Invalid value in IN clause
"select id from user where id in (18446744073709551616, 1)"
"strconv.ParseUint: parsing "18446744073709551616": value out of range"
Gen4 plan same as above

# Invalid value in IN clause from LHS of join
"select u1.id from user u1 join user u2 where u1.id = 18446744073709551616"
Expand Down