From 7d96cd95b370f78c294dc04fc14933631242074d Mon Sep 17 00:00:00 2001 From: Florent Poinsard Date: Thu, 20 May 2021 10:26:21 +0200 Subject: [PATCH 1/2] Addition of Select IN planning for Gen4 Signed-off-by: Florent Poinsard --- .../planbuilder/jointree_transformers.go | 9 ++ go/vt/vtgate/planbuilder/route_planning.go | 109 +++++++++++++----- .../planbuilder/testdata/filter_cases.txt | 11 ++ .../planbuilder/testdata/wireup_cases.txt | 1 + 4 files changed, 98 insertions(+), 32 deletions(-) diff --git a/go/vt/vtgate/planbuilder/jointree_transformers.go b/go/vt/vtgate/planbuilder/jointree_transformers.go index 44e48f47bb7..f190eae16e0 100644 --- a/go/vt/vtgate/planbuilder/jointree_transformers.go +++ b/go/vt/vtgate/planbuilder/jointree_transformers.go @@ -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 { diff --git a/go/vt/vtgate/planbuilder/route_planning.go b/go/vt/vtgate/planbuilder/route_planning.go index b528fcdcdbd..9f6eab7f923 100644 --- a/go/vt/vtgate/planbuilder/route_planning.go +++ b/go/vt/vtgate/planbuilder/route_planning.go @@ -192,8 +192,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 @@ -265,7 +266,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, @@ -292,49 +295,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)) } @@ -351,15 +401,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 } } } diff --git a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt index 47be3aeb70c..a79943f3f8b 100644 --- a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt @@ -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'))" @@ -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'))" @@ -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)" @@ -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')" @@ -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" @@ -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" @@ -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)" @@ -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'" @@ -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)" @@ -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)" @@ -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)" diff --git a/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt b/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt index d4766864a73..190692c0d06 100644 --- a/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt @@ -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" From 2865bc1add063eb360fa281ea0a3a2e41bfdef32 Mon Sep 17 00:00:00 2001 From: Florent Poinsard Date: Thu, 20 May 2021 10:54:38 +0200 Subject: [PATCH 2/2] Return not supported error for subqueries after qgraph Signed-off-by: Florent Poinsard --- go/vt/vtgate/planbuilder/route_planning.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/go/vt/vtgate/planbuilder/route_planning.go b/go/vt/vtgate/planbuilder/route_planning.go index 9f6eab7f923..09c2fe57e25 100644 --- a/go/vt/vtgate/planbuilder/route_planning.go +++ b/go/vt/vtgate/planbuilder/route_planning.go @@ -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