diff --git a/go/vt/schemadiff/diff.go b/go/vt/schemadiff/diff.go index 88eb6a7b326..ff0d861516b 100644 --- a/go/vt/schemadiff/diff.go +++ b/go/vt/schemadiff/diff.go @@ -24,6 +24,36 @@ func AllSubsequent(diff EntityDiff) (diffs []EntityDiff) { return diffs } +// AtomicDiffs attempts to break a given diff statement into its smallest components. +// This isn't necessarily the _correct_ thing to do, as MySQL goes, but it assists in +// identifying the distinct changes that are being made. +// Currently, the only implementation is to break up `ALTER TABLE ... DROP PARTITION` statements. +func AtomicDiffs(diff EntityDiff) []EntityDiff { + if diff == nil || diff.IsEmpty() { + return nil + } + trivial := func() []EntityDiff { + return []EntityDiff{diff} + } + switch diff := diff.(type) { + case *AlterTableEntityDiff: + alterTable := diff.alterTable + // Examine the scenario where we have e.g. `ALTER TABLE ... DROP PARTITION p1, p2, p3` + // and explode it into separate diffs + if alterTable.PartitionSpec != nil && alterTable.PartitionSpec.Action == sqlparser.DropAction && len(alterTable.PartitionSpec.Names) > 1 { + var distinctDiffs []EntityDiff + for i := range alterTable.PartitionSpec.Names { + clone := diff.Clone() + cloneAlterTableEntityDiff := clone.(*AlterTableEntityDiff) + cloneAlterTableEntityDiff.alterTable.PartitionSpec.Names = cloneAlterTableEntityDiff.alterTable.PartitionSpec.Names[i : i+1] + distinctDiffs = append(distinctDiffs, clone) + } + return distinctDiffs + } + } + return trivial() +} + // DiffCreateTablesQueries compares two `CREATE TABLE ...` queries (in string form) and returns the diff from table1 to table2. // Either or both of the queries can be empty. Based on this, the diff could be // nil, CreateTable, DropTable or AlterTable diff --git a/go/vt/schemadiff/table_test.go b/go/vt/schemadiff/table_test.go index 2177f1570e9..41c73ef1bc3 100644 --- a/go/vt/schemadiff/table_test.go +++ b/go/vt/schemadiff/table_test.go @@ -48,6 +48,7 @@ func TestCreateTableDiff(t *testing.T) { enumreorder int subsequent int textdiffs []string + atomicdiffs []string }{ { name: "identical", @@ -1365,6 +1366,10 @@ func TestCreateTableDiff(t *testing.T) { "-(PARTITION `p1` VALUES LESS THAN (10),", "- PARTITION `p2` VALUES LESS THAN (20),", }, + atomicdiffs: []string{ + "ALTER TABLE `t1` DROP PARTITION `p1`", + "ALTER TABLE `t1` DROP PARTITION `p2`", + }, }, { name: "change partitioning range: statements, multiple adds", @@ -1503,6 +1508,10 @@ func TestCreateTableDiff(t *testing.T) { "+ PARTITION `p3` VALUES LESS THAN (35),", "+ PARTITION `p4` VALUES LESS THAN (40))", }, + atomicdiffs: []string{ + "ALTER TABLE `t1` DROP PARTITION `p1`", + "ALTER TABLE `t1` DROP PARTITION `p3`", + }, }, { name: "change partitioning range: complex rotate 2, ignore", @@ -1523,6 +1532,10 @@ func TestCreateTableDiff(t *testing.T) { "+ PARTITION `pX` VALUES LESS THAN (30),", "+ PARTITION `p4` VALUES LESS THAN (40))", }, + atomicdiffs: []string{ + "ALTER TABLE `t1` DROP PARTITION `p1`", + "ALTER TABLE `t1` DROP PARTITION `p3`", + }, }, { name: "change partitioning range: not a rotation", @@ -2072,6 +2085,7 @@ func TestCreateTableDiff(t *testing.T) { t.Logf("other: %v", sqlparser.CanonicalString(other.CreateTable)) } assert.Empty(t, ts.textdiffs) + assert.Empty(t, AtomicDiffs(alter)) return } @@ -2125,6 +2139,18 @@ func TestCreateTableDiff(t *testing.T) { applied.Create().CanonicalStatementString(), ) } + // Validate atomic diffs + atomicDiffs := AtomicDiffs(alter) + if len(ts.atomicdiffs) > 0 { + assert.Equal(t, len(ts.atomicdiffs), len(atomicDiffs), "%+v", atomicDiffs) + for i := range ts.atomicdiffs { + assert.Equal(t, ts.atomicdiffs[i], atomicDiffs[i].CanonicalStatementString()) + } + } else { + assert.Equal(t, 1, len(atomicDiffs)) + assert.Equal(t, alter.CanonicalStatementString(), atomicDiffs[0].CanonicalStatementString()) + } + { // Validate annotations alterEntityDiff, ok := alter.(*AlterTableEntityDiff) require.True(t, ok)