diff --git a/dump/dumper.go b/dump/dumper.go index dda9b8957..b56a44a0b 100644 --- a/dump/dumper.go +++ b/dump/dumper.go @@ -6,6 +6,7 @@ import ( "io" "os" "os/exec" + "regexp" "strings" . "github.com/go-mysql-org/go-mysql/mysql" @@ -44,6 +45,9 @@ type Dumper struct { // see detectColumnStatisticsParamSupported isColumnStatisticsParamSupported bool + + mysqldumpVersion string + sourceDataSupported bool } func NewDumper(executionPath string, addr string, user string, password string) (*Dumper, error) { @@ -67,7 +71,14 @@ func NewDumper(executionPath string, addr string, user string, password string) d.IgnoreTables = make(map[string][]string) d.ExtraOptions = make([]string, 0, 5) d.masterDataSkipped = false - d.isColumnStatisticsParamSupported = d.detectColumnStatisticsParamSupported() + + out, err := exec.Command(d.ExecutionPath, `--help`).CombinedOutput() + if err != nil { + return d, err + } + d.isColumnStatisticsParamSupported = d.detectColumnStatisticsParamSupported(out) + d.mysqldumpVersion = d.getMysqldumpVersion(out) + d.sourceDataSupported = d.detectSourceDataSupported(d.mysqldumpVersion) d.ErrOut = os.Stderr @@ -81,12 +92,47 @@ func NewDumper(executionPath string, addr string, user string, password string) // But this parameter exists only for versions >=8.0.2 (https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-2.html). // // For environments where the version of mysql-server and mysqldump differs, we try to check this parameter and use it if available. -func (d *Dumper) detectColumnStatisticsParamSupported() bool { - out, err := exec.Command(d.ExecutionPath, `--help`).CombinedOutput() - if err != nil { +func (d *Dumper) detectColumnStatisticsParamSupported(helpOutput []byte) bool { + return bytes.Contains(helpOutput, []byte(`--column-statistics`)) +} + +// mysqldump Ver 10.19 Distrib 10.3.37-MariaDB, for linux-systemd (x86_64)`, `10.3.37-MariaDB +// opt/mysql/11.0.0/bin/mysqldump from 11.0.0-preview-MariaDB, client 10.19 for linux-systemd (x86_64) +func (d *Dumper) getMysqldumpVersion(helpOutput []byte) string { + mysqldumpVersionRegexpNew := regexp.MustCompile(`mysqldump Ver ([0-9][^ ]*) for`) + if m := mysqldumpVersionRegexpNew.FindSubmatch(helpOutput); m != nil { + return string(m[1]) + } + + mysqldumpVersionRegexpOld := regexp.MustCompile(`mysqldump Ver .* Distrib ([0-9][^ ]*),`) + if m := mysqldumpVersionRegexpOld.FindSubmatch(helpOutput); m != nil { + return string(m[1]) + } + + mysqldumpVersionRegexpMaria := regexp.MustCompile(`mysqldump from ([0-9][^ ]*), `) + if m := mysqldumpVersionRegexpMaria.FindSubmatch(helpOutput); m != nil { + return string(m[1]) + } + + return "" +} + +func (d *Dumper) detectSourceDataSupported(version string) bool { + // Failed to detect mysqldump version + if version == "" { + return false + } + + // MySQL 5.x + if version[0] == byte('5') { + return false + } + + if strings.Contains(version, "MariaDB") { return false } - return bytes.Contains(out, []byte(`--column-statistics`)) + + return true } func (d *Dumper) SetCharset(charset string) { @@ -169,7 +215,11 @@ func (d *Dumper) Dump(w io.Writer) error { passwordArgIndex := len(args) - 1 if !d.masterDataSkipped { - args = append(args, "--master-data") + if d.sourceDataSupported { + args = append(args, "--source-data") + } else { + args = append(args, "--master-data") + } } if d.maxAllowedPacket > 0 { diff --git a/dump/dumper_test.go b/dump/dumper_test.go new file mode 100644 index 000000000..9e2301315 --- /dev/null +++ b/dump/dumper_test.go @@ -0,0 +1,62 @@ +package dump + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetMysqldumpVersion(t *testing.T) { + versions := []struct { + line string // mysqldump --help | head -1 + version string // 9.1.0 + }{ + // Oracle MySQL + {`mysqldump Ver 10.13 Distrib 5.5.62, for linux-glibc2.12 (x86_64)`, `5.5.62`}, + {`mysqldump Ver 10.13 Distrib 5.6.44, for linux-glibc2.12 (x86_64)`, `5.6.44`}, + {`mysqldump Ver 10.13 Distrib 5.7.31, for linux-glibc2.12 (x86_64)`, `5.7.31`}, + {`mysqldump Ver 10.13 Distrib 5.7.36, for linux-glibc2.12 (x86_64)`, `5.7.36`}, + {`mysqldump Ver 8.0.11 for linux-glibc2.12 on x86_64 (MySQL Community Server - GPL)`, `8.0.11`}, + {`mysqldump Ver 8.0.22 for Linux on x86_64 (MySQL Community Server - GPL)`, `8.0.22`}, + {`mysqldump Ver 8.0.25 for Linux on x86_64 (MySQL Community Server - GPL)`, `8.0.25`}, + {`mysqldump Ver 8.0.26 for Linux on x86_64 (MySQL Community Server - GPL)`, `8.0.26`}, + {`mysqldump Ver 8.0.27 for Linux on x86_64 (MySQL Community Server - GPL)`, `8.0.27`}, + {`mysqldump Ver 8.0.28 for Linux on x86_64 (MySQL Community Server - GPL)`, `8.0.28`}, + {`mysqldump Ver 8.0.31 for Linux on x86_64 (Source distribution)`, `8.0.31`}, + {`mysqldump Ver 8.0.32 for Linux on x86_64 (MySQL Community Server - GPL)`, `8.0.32`}, + {`mysqldump Ver 8.4.2 for FreeBSD14.0 on amd64 (Source distribution)`, `8.4.2`}, + {`mysqldump Ver 9.1.0 for Linux on x86_64 (MySQL Community Server - GPL)`, `9.1.0`}, + + // MariaDB + {`mysqldump Ver 10.19 Distrib 10.3.37-MariaDB, for linux-systemd (x86_64)`, `10.3.37-MariaDB`}, + {`mysqldump Ver 10.19 Distrib 10.6.11-MariaDB, for linux-systemd (x86_64)`, `10.6.11-MariaDB`}, + {`opt/mysql/11.0.0/bin/mysqldump from 11.0.0-preview-MariaDB, client 10.19 for linux-systemd (x86_64)`, `11.0.0-preview-MariaDB`}, + {`opt/mysql/11.2.2/bin/mysqldump from 11.2.2-MariaDB, client 10.19 for linux-systemd (x86_64)`, `11.2.2-MariaDB`}, + } + + d := new(Dumper) + for _, v := range versions { + ver := d.getMysqldumpVersion([]byte(v.line)) + require.Equal(t, v.version, ver, v.line) + } +} + +func TestDetectSourceDataSupported(t *testing.T) { + versions := []struct { + version string + supported bool + }{ + {`5.7.40`, false}, + {`8.0.11`, true}, + {`8.4.1`, true}, + {`9.1.0`, true}, + {``, false}, + {`10.3.37-MariaDB`, false}, + {`11.2.2-MariaDB`, false}, + } + + d := new(Dumper) + for _, v := range versions { + require.Equal(t, v.supported, d.detectSourceDataSupported(v.version), v.version) + } +}