From 05d7e768e88972bcafba352c4af6bf2b9cde888a Mon Sep 17 00:00:00 2001 From: Matt Lord Date: Tue, 2 Jul 2024 10:26:00 -0400 Subject: [PATCH] VDiff: Copy non in_keyrange workflow filters to target tablet query (#16307) Signed-off-by: Matt Lord --- .../tabletmanager/vdiff/table_plan.go | 10 +++++++-- go/vt/vttablet/tabletmanager/vdiff/utils.go | 21 +++++++++++++++++++ .../vdiff/workflow_differ_test.go | 12 ++++------- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/vdiff/table_plan.go b/go/vt/vttablet/tabletmanager/vdiff/table_plan.go index 548f902e9ac..becce6f90e6 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/table_plan.go +++ b/go/vt/vttablet/tabletmanager/vdiff/table_plan.go @@ -174,8 +174,14 @@ func (td *tableDiffer) buildTablePlan(dbClient binlogplayer.DBClient, dbName str return nil, err } - // Remove in_keyrange. It's not understood by mysql. - sourceSelect.Where = sel.Where // removeKeyrange(sel.Where) + // Copy all workflow filters for the source query. + sourceSelect.Where = sel.Where + + // Copy all non-in_keyrange workflow filters to the target query. + // This is important for things like multi-tenant migrations where + // an additional tenant_id filter is applied in the workflow. + targetSelect.Where = copyNonKeyRangeExpressions(sel.Where) + // The source should also perform the group by. sourceSelect.GroupBy = sel.GroupBy sourceSelect.OrderBy = tp.orderBy diff --git a/go/vt/vttablet/tabletmanager/vdiff/utils.go b/go/vt/vttablet/tabletmanager/vdiff/utils.go index 07e070976a9..aeaa28972e0 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/utils.go +++ b/go/vt/vttablet/tabletmanager/vdiff/utils.go @@ -21,6 +21,7 @@ import ( "fmt" "strings" + "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/binlog/binlogplayer" @@ -89,3 +90,23 @@ func stringListContains(lst []string, item string) bool { } return contains } + +// copyNonKeyRangeExpressions copies all expressions from the input WHERE clause +// to the output WHERE clause except for any in_keyrange() expressions. +func copyNonKeyRangeExpressions(where *sqlparser.Where) *sqlparser.Where { + if where == nil { + return nil + } + exprs := sqlparser.SplitAndExpression(nil, where.Expr) + newWhere := &sqlparser.Where{} + for _, expr := range exprs { + switch expr := expr.(type) { + case *sqlparser.FuncExpr: + if expr.Name.EqualString("in_keyrange") { + continue + } + } + newWhere.Expr = sqlparser.AndExpressions(newWhere.Expr, expr) + } + return newWhere +} diff --git a/go/vt/vttablet/tabletmanager/vdiff/workflow_differ_test.go b/go/vt/vttablet/tabletmanager/vdiff/workflow_differ_test.go index a460b87a4f6..d4f9ddb001d 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/workflow_differ_test.go +++ b/go/vt/vttablet/tabletmanager/vdiff/workflow_differ_test.go @@ -364,7 +364,6 @@ func TestBuildPlanSuccess(t *testing.T) { }, }, { // in_keyrange on RHS of AND. - // This is currently not a valid construct, but will be supported in the future. input: &binlogdatapb.Rule{ Match: "t1", Filter: "select * from t1 where c2 = 2 and in_keyrange('-80')", @@ -374,7 +373,7 @@ func TestBuildPlanSuccess(t *testing.T) { dbName: vdiffDBName, table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c1, c2 from t1 where c2 = 2 and in_keyrange('-80') order by c1 asc", - targetQuery: "select c1, c2 from t1 order by c1 asc", + targetQuery: "select c1, c2 from t1 where c2 = 2 order by c1 asc", compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, @@ -386,7 +385,6 @@ func TestBuildPlanSuccess(t *testing.T) { }, }, { // in_keyrange on LHS of AND. - // This is currently not a valid construct, but will be supported in the future. input: &binlogdatapb.Rule{ Match: "t1", Filter: "select * from t1 where in_keyrange('-80') and c2 = 2", @@ -396,7 +394,7 @@ func TestBuildPlanSuccess(t *testing.T) { dbName: vdiffDBName, table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c1, c2 from t1 where in_keyrange('-80') and c2 = 2 order by c1 asc", - targetQuery: "select c1, c2 from t1 order by c1 asc", + targetQuery: "select c1, c2 from t1 where c2 = 2 order by c1 asc", compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, @@ -408,7 +406,6 @@ func TestBuildPlanSuccess(t *testing.T) { }, }, { // in_keyrange on cascaded AND expression. - // This is currently not a valid construct, but will be supported in the future. input: &binlogdatapb.Rule{ Match: "t1", Filter: "select * from t1 where c2 = 2 and c1 = 1 and in_keyrange('-80')", @@ -418,7 +415,7 @@ func TestBuildPlanSuccess(t *testing.T) { dbName: vdiffDBName, table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c1, c2 from t1 where c2 = 2 and c1 = 1 and in_keyrange('-80') order by c1 asc", - targetQuery: "select c1, c2 from t1 order by c1 asc", + targetQuery: "select c1, c2 from t1 where c2 = 2 and c1 = 1 order by c1 asc", compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0}, @@ -430,7 +427,6 @@ func TestBuildPlanSuccess(t *testing.T) { }, }, { // in_keyrange parenthesized. - // This is currently not a valid construct, but will be supported in the future. input: &binlogdatapb.Rule{ Match: "t1", Filter: "select * from t1 where (c2 = 2 and in_keyrange('-80'))", @@ -440,7 +436,7 @@ func TestBuildPlanSuccess(t *testing.T) { dbName: vdiffDBName, table: testSchema.TableDefinitions[tableDefMap["t1"]], sourceQuery: "select c1, c2 from t1 where c2 = 2 and in_keyrange('-80') order by c1 asc", - targetQuery: "select c1, c2 from t1 order by c1 asc", + targetQuery: "select c1, c2 from t1 where c2 = 2 order by c1 asc", compareCols: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}, {1, collations.MySQL8().LookupByName(sqltypes.NULL.String()), false, "c2"}}, comparePKs: []compareColInfo{{0, collations.MySQL8().LookupByName(sqltypes.NULL.String()), true, "c1"}}, pkCols: []int{0},