diff --git a/go/logic/inspect.go b/go/logic/inspect.go index da21feb..b0605f1 100644 --- a/go/logic/inspect.go +++ b/go/logic/inspect.go @@ -23,6 +23,35 @@ import ( const startSlavePostWaitMilliseconds = 500 * time.Millisecond +var grantOnRegexp = regexp.MustCompile(" ON `(.*?)`\\.\\*") + +func grantMatch(grant string, databaseName string) bool { + matches := grantOnRegexp.FindStringSubmatch(grant) + if matches == nil { + return false + } + mysqlPattern := matches[1] + regexPattern := "^" + escape := false + for _, c := range mysqlPattern { + if escape { + regexPattern += regexp.QuoteMeta(string(c)) + escape = false + } else if c == '%' { + regexPattern += ".*" + } else if c == '_' { + regexPattern += "." + } else if c == '\\' { + escape = true + } else { + regexPattern += regexp.QuoteMeta(string(c)) + } + } + regexPattern += "$" + match, _ := regexp.MatchString(regexPattern, databaseName) + return match +} + // Inspector reads data from the read-MySQL-server (typically a replica, but can be the master) // It is used for gaining initial status and structure, and later also follow up on progress and changelog type Inspector struct { @@ -31,7 +60,6 @@ type Inspector struct { informationSchemaDb *gosql.DB migrationContext *base.MigrationContext name string - grantOnRe *regexp.Regexp } func NewInspector(migrationContext *base.MigrationContext) *Inspector { @@ -39,7 +67,6 @@ func NewInspector(migrationContext *base.MigrationContext) *Inspector { connectionConfig: migrationContext.InspectorConnectionConfig, migrationContext: migrationContext, name: "inspector", - grantOnRe: regexp.MustCompile(" ON `(.*?)`\\.\\*"), } } @@ -281,33 +308,7 @@ func (this *Inspector) grantContainsAll(grant string, substrings ...string) bool if !base.StringContainsAll(grant, substrings...) { return false } - if strings.Contains(grant, ` ON *.*`) { - return true - } - matches := this.grantOnRe.FindStringSubmatch(grant) - if matches == nil { - return false - } - mysqlPattern := matches[1] - regexPattern := "^" - escape := false - for _, c := range mysqlPattern { - if escape { - regexPattern += regexp.QuoteMeta(string(c)) - escape = false - } else if c == '%' { - regexPattern += ".*" - } else if c == '_' { - regexPattern += "." - } else if c == '\\' { - escape = true - } else { - regexPattern += regexp.QuoteMeta(string(c)) - } - } - regexPattern += "$" - match, _ := regexp.MatchString(regexPattern, this.migrationContext.DatabaseName) - return match + return strings.Contains(grant, ` ON *.*`) || grantMatch(grant, this.migrationContext.DatabaseName) } // restartReplication is required so that we are _certain_ the binlog format and diff --git a/go/logic/inspect_test.go b/go/logic/inspect_test.go new file mode 100644 index 0000000..7afde3d --- /dev/null +++ b/go/logic/inspect_test.go @@ -0,0 +1,31 @@ +/* + Copyright 2022 GitHub Inc. + See https://github.com/github/gh-ost/blob/master/LICENSE +*/ + +package logic + +import ( + "testing" + + "github.com/openark/golib/log" + test "github.com/openark/golib/tests" +) + +func init() { + log.SetLevel(log.ERROR) +} + +func TestGrantMatch(t *testing.T) { + databaseName := `my_database%name` + + test.S(t).ExpectFalse(grantMatch("GRANT SELECT ON *.* TO 'user'@'%'", databaseName)) + test.S(t).ExpectFalse(grantMatch("GRANT SELECT ON `other\\_database\\%name`.* TO 'user'@'%'", databaseName)) + test.S(t).ExpectTrue(grantMatch("GRANT SELECT ON `my\\_database\\%name`.* TO 'user'@'%'", databaseName)) + test.S(t).ExpectTrue(grantMatch("GRANT SELECT ON `my_database_name`.* TO 'user'@'%'", databaseName)) + test.S(t).ExpectTrue(grantMatch("GRANT SELECT ON `my\\_database\\%%`.* TO 'user'@'%'", databaseName)) + test.S(t).ExpectFalse(grantMatch("GRANT SELECT ON `database\\%%`.* TO 'user'@'%'", databaseName)) + test.S(t).ExpectTrue(grantMatch("GRANT SELECT ON `my\\_database\\%____`.* TO 'user'@'%'", databaseName)) + test.S(t).ExpectFalse(grantMatch("GRANT SELECT ON `my\\_database\\%_`.* TO 'user'@'%'", databaseName)) + test.S(t).ExpectTrue(grantMatch("GRANT SELECT ON `%database%`.* TO 'user'@'%'", databaseName)) +}