diff --git a/go/vt/vtgate/planbuilder/physical/route.go b/go/vt/vtgate/planbuilder/physical/route.go index 871324b0421..3f27907214a 100644 --- a/go/vt/vtgate/planbuilder/physical/route.go +++ b/go/vt/vtgate/planbuilder/physical/route.go @@ -600,6 +600,8 @@ func (r *Route) planCompositeInOpRecursive( return foundVindex } +// Reset all vindex predicates on this route and re-build their options from +// the list of seen routing predicates. func (r *Route) resetRoutingSelections(ctx *plancontext.PlanningContext) error { switch r.RouteOpCode { case engine.DBA, engine.Next, engine.Reference, engine.Unsharded: diff --git a/go/vt/vtgate/planbuilder/physical/route_planning.go b/go/vt/vtgate/planbuilder/physical/route_planning.go index aaa7847c95d..5b52a89a007 100644 --- a/go/vt/vtgate/planbuilder/physical/route_planning.go +++ b/go/vt/vtgate/planbuilder/physical/route_planning.go @@ -562,6 +562,7 @@ func createRouteOperatorForJoin(aRoute, bRoute *Route, joinPredicates []sqlparse Keyspace: aRoute.Keyspace, VindexPreds: append(aRoute.VindexPreds, bRoute.VindexPreds...), SysTableTableSchema: append(aRoute.SysTableTableSchema, bRoute.SysTableTableSchema...), + SeenPredicates: append(aRoute.SeenPredicates, bRoute.SeenPredicates...), SysTableTableName: sysTableName, Source: &ApplyJoin{ LHS: aRoute.Source, diff --git a/go/vt/vtgate/planbuilder/physical/subquery_planning.go b/go/vt/vtgate/planbuilder/physical/subquery_planning.go index 4177274cc7b..88d38bc4bae 100644 --- a/go/vt/vtgate/planbuilder/physical/subquery_planning.go +++ b/go/vt/vtgate/planbuilder/physical/subquery_planning.go @@ -107,6 +107,22 @@ func mergeSubQueryOp(ctx *plancontext.PlanningContext, outer *Route, inner *Rout outer.SysTableTableName[k] = v } + // When merging an inner query with its outer query, we can remove the + // inner query from the list of predicates that can influence routing of + // the outer query. + // + // Note that not all inner queries necessarily are part of the routing + // predicates list, so this might be a no-op. + for i, predicate := range outer.SeenPredicates { + if sqlparser.EqualsExpr(predicate, subq.ExtractedSubquery) { + outer.SeenPredicates = append(outer.SeenPredicates[:i], outer.SeenPredicates[i+1:]...) + + // The `ExtractedSubquery` of an inner query is unique (due to the uniqueness of bind variable names) + // so we can stop after the first match. + break + } + } + err = outer.resetRoutingSelections(ctx) if err != nil { return nil, err diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.txt b/go/vt/vtgate/planbuilder/testdata/select_cases.txt index 28eebbdf094..c5f7565a513 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.txt @@ -4114,6 +4114,51 @@ Gen4 plan same as above "unsupported: cross-shard correlated subquery" Gen4 error: exists sub-queries are only supported with AND clause +# correlated subquery that is dependent on one side of a join, fully mergeable +"SELECT music.id FROM music INNER JOIN user ON music.user_id = user.id WHERE music.user_id = 5 AND music.id = (SELECT MAX(m2.id) FROM music m2 WHERE m2.user_id = user.id)" +{ + "QueryType": "SELECT", + "Original": "SELECT music.id FROM music INNER JOIN user ON music.user_id = user.id WHERE music.user_id = 5 AND music.id = (SELECT MAX(m2.id) FROM music m2 WHERE m2.user_id = user.id)", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select music.id from music join `user` on music.user_id = `user`.id where 1 != 1", + "Query": "select music.id from music join `user` on music.user_id = `user`.id where music.user_id = 5 and music.id = (select max(m2.id) from music as m2 where m2.user_id = `user`.id)", + "Table": "music, `user`", + "Values": [ + "INT64(5)" + ], + "Vindex": "user_index" + } +} +{ + "QueryType": "SELECT", + "Original": "SELECT music.id FROM music INNER JOIN user ON music.user_id = user.id WHERE music.user_id = 5 AND music.id = (SELECT MAX(m2.id) FROM music m2 WHERE m2.user_id = user.id)", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select music.id from music, `user` where 1 != 1", + "Query": "select music.id from music, `user` where music.user_id = 5 and music.id = (select max(m2.id) from music as m2 where m2.user_id = `user`.id) and music.user_id = `user`.id", + "Table": "`user`, music", + "Values": [ + "INT64(5)" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "user.music", + "user.user" + ] +} + # union as a derived table "select found from (select id as found from user union all (select id from unsharded)) as t" {