diff --git a/go/test/endtoend/reparent/plannedreparent/reparent_test.go b/go/test/endtoend/reparent/plannedreparent/reparent_test.go index ae9bd6bbc9b..3527df604c8 100644 --- a/go/test/endtoend/reparent/plannedreparent/reparent_test.go +++ b/go/test/endtoend/reparent/plannedreparent/reparent_test.go @@ -199,13 +199,13 @@ func TestReparentFromOutsideWithNoPrimary(t *testing.T) { } func reparentFromOutside(t *testing.T, clusterInstance *cluster.LocalProcessCluster, downPrimary bool) { - //This test will start a primary and 3 replicas. - //Then: - //- one replica will be the new primary - //- one replica will be reparented to that new primary - //- one replica will be busted and dead in the water and we'll call TabletExternallyReparented. - //Args: - //downPrimary: kills the old primary first + // This test will start a primary and 3 replicas. + // Then: + // - one replica will be the new primary + // - one replica will be reparented to that new primary + // - one replica will be busted and dead in the water and we'll call TabletExternallyReparented. + // Args: + // downPrimary: kills the old primary first ctx := context.Background() tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets @@ -218,7 +218,7 @@ func reparentFromOutside(t *testing.T, clusterInstance *cluster.LocalProcessClus demoteCommands := []string{"SET GLOBAL read_only = ON", "FLUSH TABLES WITH READ LOCK", "UNLOCK TABLES"} utils.RunSQLs(ctx, t, demoteCommands, tablets[0]) - //Get the position of the old primary and wait for the new one to catch up. + // Get the position of the old primary and wait for the new one to catch up. err := utils.WaitForReplicationPosition(t, tablets[0], tablets[1]) require.NoError(t, err) } @@ -304,7 +304,11 @@ func TestReparentWithDownReplica(t *testing.T) { // insert data into the new primary, check the connected replica work insertVal = utils.ConfirmReplication(t, tablets[1], []*cluster.Vttablet{tablets[0], tablets[3]}) } else { - assert.Contains(t, out, fmt.Sprintf("TabletManager.PrimaryStatus on %s", tablets[2].Alias)) + if clusterInstance.VtctlMajorVersion <= 20 { + assert.Contains(t, out, fmt.Sprintf("TabletManager.PrimaryStatus on %s", tablets[2].Alias)) + } else { + assert.Contains(t, out, fmt.Sprintf("TabletManager.GetGlobalStatusVars on %s", tablets[2].Alias)) + } // insert data into the old primary, check the connected replica works. The primary tablet shouldn't have changed. insertVal = utils.ConfirmReplication(t, tablets[0], []*cluster.Vttablet{tablets[1], tablets[3]}) } diff --git a/go/test/endtoend/vtgate/queries/misc/misc_test.go b/go/test/endtoend/vtgate/queries/misc/misc_test.go index dc789f3d1bb..157fa549e37 100644 --- a/go/test/endtoend/vtgate/queries/misc/misc_test.go +++ b/go/test/endtoend/vtgate/queries/misc/misc_test.go @@ -382,6 +382,9 @@ func TestAliasesInOuterJoinQueries(t *testing.T) { mcmp.ExecWithColumnCompare("select t1.id1 as t0, t1.id1 as t1, tbl.unq_col as col from t1 left outer join tbl on t1.id2 = tbl.nonunq_col order by t1.id2 limit 2 offset 2") mcmp.ExecWithColumnCompare("select t1.id1 as t0, t1.id1 as t1, count(*) as leCount from t1 left outer join tbl on t1.id2 = tbl.nonunq_col group by 1, t1") mcmp.ExecWithColumnCompare("select t.id1, t.id2, derived.unq_col from t1 t join (select id, unq_col, nonunq_col from tbl) as derived on t.id2 = derived.nonunq_col") + if utils.BinaryIsAtLeastAtVersion(20, "vtgate") { + mcmp.ExecWithColumnCompare("select * from t1 t left join tbl on t.id1 = 666 and t.id2 = tbl.id") + } } func TestAlterTableWithView(t *testing.T) { diff --git a/go/vt/vtgate/planbuilder/operators/apply_join.go b/go/vt/vtgate/planbuilder/operators/apply_join.go index 0d214c545d1..03d1736e6ef 100644 --- a/go/vt/vtgate/planbuilder/operators/apply_join.go +++ b/go/vt/vtgate/planbuilder/operators/apply_join.go @@ -37,8 +37,6 @@ type ( // JoinType is permitted to store only 3 of the possible values // NormalJoinType, StraightJoinType and LeftJoinType. JoinType sqlparser.JoinType - // LeftJoin will be true in the case of an outer join - LeftJoin bool // JoinColumns keeps track of what AST expression is represented in the Columns array JoinColumns *applyJoinColumns @@ -367,6 +365,10 @@ func (aj *ApplyJoin) ShortDescription() string { } firstPart := fmt.Sprintf("on %s columns: %s", fn(aj.JoinPredicates), fn(aj.JoinColumns)) + if aj.JoinType == sqlparser.LeftJoinType { + firstPart = "LEFT JOIN " + firstPart + } + if len(aj.ExtraLHSVars) == 0 { return firstPart } diff --git a/go/vt/vtgate/planbuilder/operators/route_planning.go b/go/vt/vtgate/planbuilder/operators/route_planning.go index 47405e3b935..22db69f287b 100644 --- a/go/vt/vtgate/planbuilder/operators/route_planning.go +++ b/go/vt/vtgate/planbuilder/operators/route_planning.go @@ -305,13 +305,18 @@ func mergeOrJoin(ctx *plancontext.PlanningContext, lhs, rhs Operator, joinPredic } join := NewApplyJoin(ctx, Clone(rhs), Clone(lhs), nil, joinType) - newOp := pushJoinPredicates(ctx, joinPredicates, join) - return newOp, Rewrote("logical join to applyJoin, switching side because LIMIT") + for _, pred := range joinPredicates { + join.AddJoinPredicate(ctx, pred) + } + return join, Rewrote("logical join to applyJoin, switching side because LIMIT") } join := NewApplyJoin(ctx, Clone(lhs), Clone(rhs), nil, joinType) - newOp := pushJoinPredicates(ctx, joinPredicates, join) - return newOp, Rewrote("logical join to applyJoin ") + for _, pred := range joinPredicates { + join.AddJoinPredicate(ctx, pred) + } + + return join, Rewrote("logical join to applyJoin ") } func operatorsToRoutes(a, b Operator) (*Route, *Route) { @@ -530,15 +535,3 @@ func hexEqual(a, b *sqlparser.Literal) bool { } return false } - -func pushJoinPredicates(ctx *plancontext.PlanningContext, exprs []sqlparser.Expr, op *ApplyJoin) Operator { - if len(exprs) == 0 { - return op - } - - for _, expr := range exprs { - AddPredicate(ctx, op, expr, true, newFilterSinglePredicate) - } - - return op -} diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.json b/go/vt/vtgate/planbuilder/testdata/from_cases.json index 88a4ebd7027..b9670910f10 100644 --- a/go/vt/vtgate/planbuilder/testdata/from_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/from_cases.json @@ -784,6 +784,59 @@ ] } }, + { + "comment": "Outer join with join predicates that only depend on the inner side", + "query": "select 1 from user left join user_extra on user.foo = 42 and user.bar = user_extra.bar", + "plan": { + "QueryType": "SELECT", + "Original": "select 1 from user left join user_extra on user.foo = 42 and user.bar = user_extra.bar", + "Instructions": { + "OperatorType": "Projection", + "Expressions": [ + "1 as 1" + ], + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "LeftJoin", + "JoinVars": { + "user_bar": 1, + "user_foo": 0 + }, + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `user`.foo, `user`.bar from `user` where 1 != 1", + "Query": "select `user`.foo, `user`.bar from `user`", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from user_extra where 1 != 1", + "Query": "select 1 from user_extra where user_extra.bar = :user_bar and :user_foo = 42", + "Table": "user_extra" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, { "comment": "Parenthesized, single chunk", "query": "select user.col from user join (unsharded as m1 join unsharded as m2)",