Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MM-62662] add ability to compare tables with matching names only #8

Merged
merged 3 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cmd/dbcmp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func main() {
rootCmd.PersistentFlags().String("source", "", "source database dsn")
rootCmd.PersistentFlags().String("target", "", "target database dsn")
rootCmd.Flags().StringSlice("exclude", []string{}, "exclude tables from comparison, takes comma-separated values.")
rootCmd.Flags().StringSlice("include", []string{}, "include only matching tables for comparison, takes comma-separated values.")
rootCmd.Flags().Int("page-size", 1000, "page size for each checksum comparison.")

if err := rootCmd.Execute(); err != nil {
Expand All @@ -51,6 +52,11 @@ func runRootCmdFn(cmd *cobra.Command, args []string) error {
return err
}

incl, err := cmd.Flags().GetStringSlice("include")
if err != nil {
return err
}

pageSize, err := cmd.Flags().GetInt("page-size")
if err != nil {
return err
Expand All @@ -62,6 +68,7 @@ func runRootCmdFn(cmd *cobra.Command, args []string) error {

diffs, err := store.Compare(source, target, store.CompareOptions{
ExcludePatterns: excl,
IncludePatterns: incl,
PageSize: pageSize,
})
if err != nil {
Expand Down
11 changes: 11 additions & 0 deletions internal/store/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

type CompareOptions struct {
ExcludePatterns []string
IncludePatterns []string
Verbose bool
PageSize int
}
Expand Down Expand Up @@ -35,6 +36,11 @@ func Compare(srcDSN, dstDSN string, opts CompareOptions) ([]string, error) {
}

excl := sliceToMap(opts.ExcludePatterns)
incl := sliceToMap(opts.IncludePatterns)

if len(incl) > 0 && len(excl) > 0 {
return nil, fmt.Errorf("include and exclude flags cannot be used together")
}

// find a more elegant solution fo this
// essentially we want to exclude some
Expand All @@ -45,6 +51,11 @@ func Compare(srcDSN, dstDSN string, opts CompareOptions) ([]string, error) {
delete(srcTables, k)
}
}
for e := range incl {
if !strings.Contains(k, strings.ToLower(e)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the flag documentation, it looked like we should be doing an exact match? In that case, we could probably build up a map and check for this faster, rather than having 2 loops. But, not a blocker.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I slightly changed the logic, for include scenario it should be an exact match. PTAL @agnivade

delete(srcTables, k)
}
}
}

var mismatchs []string
Expand Down
99 changes: 70 additions & 29 deletions internal/store/compare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,48 +8,89 @@ import (
)

func TestCompare(t *testing.T) {
// compare empty databases
mismatches, err := Compare(mysqlTestDSN, pgsqlTestDSN, CompareOptions{})
require.NoError(t, err)
require.Empty(t, mismatches)
t.Run("Compare empty database", func(t *testing.T) {
mismatches, err := Compare(mysqlTestDSN, pgsqlTestDSN, CompareOptions{})
require.NoError(t, err)
require.Empty(t, mismatches)
})

// compare empty databases
mismatches, err = Compare(mysqlLegacyTestDSN, pgsqlTestDSN, CompareOptions{})
require.NoError(t, err)
require.Empty(t, mismatches)
t.Run("Compare empty database (legacy)", func(t *testing.T) {
mismatches, err := Compare(mysqlLegacyTestDSN, pgsqlTestDSN, CompareOptions{})
require.NoError(t, err)
require.Empty(t, mismatches)
})

ec := rand.Intn(100) + 20 // we add 20 to ensure pagination gets triggered
h := newTestHelper(t).SeedTableData(ec)
defer h.Teardown()

mismatches, err = Compare(mysqlTestDSN, pgsqlTestDSN, CompareOptions{
PageSize: 20,
t.Run("Compare databases with same data", func(t *testing.T) {
mismatches, err := Compare(mysqlTestDSN, pgsqlTestDSN, CompareOptions{
PageSize: 20,
})
require.NoError(t, err)
require.Empty(t, mismatches)
})
require.NoError(t, err)
require.Empty(t, mismatches)

mismatches, err = Compare(mysqlLegacyTestDSN, pgsqlTestDSN, CompareOptions{
PageSize: 20,
t.Run("Compare databases with same data (legacy)", func(t *testing.T) {
mismatches, err := Compare(mysqlLegacyTestDSN, pgsqlTestDSN, CompareOptions{
PageSize: 20,
})
require.NoError(t, err)
require.Empty(t, mismatches)
})
require.NoError(t, err)
require.Empty(t, mismatches)

mismatches, err = Compare(pgsqlTestDSN, mysqlTestDSN, CompareOptions{
PageSize: 20,
t.Run("Compare databases with other way around", func(t *testing.T) {
mismatches, err := Compare(pgsqlTestDSN, mysqlTestDSN, CompareOptions{
PageSize: 20,
})
require.NoError(t, err)
require.Empty(t, mismatches)
})
require.NoError(t, err)
require.Empty(t, mismatches)

mysqldb, ok := h.dbInstances["mysql"]
require.True(t, ok)
t.Run("Compare databases when there is data change", func(t *testing.T) {
mysqldb, ok := h.dbInstances["mysql"]
require.True(t, ok)

// delete random entry
_, err := mysqldb.sqlDB.Query("DELETE FROM Table1 LIMIT 1")
require.NoError(t, err)

mismatches, err := Compare(pgsqlTestDSN, mysqlTestDSN, CompareOptions{
PageSize: 20,
})
require.NoError(t, err)
require.Len(t, mismatches, 1)
})

t.Run("Assert exclude and include flags", func(t *testing.T) {
// test with exclude patterns
mismatches, err := Compare(mysqlTestDSN, pgsqlTestDSN, CompareOptions{
ExcludePatterns: []string{"Table1"},
})
require.NoError(t, err)
require.Empty(t, mismatches)

// Table2 is the same
mismatches, err = Compare(mysqlTestDSN, pgsqlTestDSN, CompareOptions{
IncludePatterns: []string{"Table2"},
})
require.NoError(t, err)
require.Empty(t, mismatches)

// delete random entry
_, err = mysqldb.sqlDB.Query("DELETE FROM Table1 LIMIT 1")
require.NoError(t, err)
// test with include patterns
mismatches, err = Compare(mysqlTestDSN, pgsqlTestDSN, CompareOptions{
IncludePatterns: []string{"Table1"},
})
require.NoError(t, err)
require.Len(t, mismatches, 1)

mismatches, err = Compare(pgsqlTestDSN, mysqlTestDSN, CompareOptions{
PageSize: 20,
// test with both include and exclude patterns
_, err = Compare(mysqlTestDSN, pgsqlTestDSN, CompareOptions{
IncludePatterns: []string{"Table1"},
ExcludePatterns: []string{"Table2"},
})
require.Error(t, err)
require.Contains(t, err.Error(), "include and exclude flags cannot be used together")
})
require.NoError(t, err)
require.Len(t, mismatches, 1)
}