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

allow validate password variables #366

Merged
merged 2 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
109 changes: 62 additions & 47 deletions go/vt/sqlparser/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -6852,6 +6852,22 @@ func VarScopeForColName(colName *ColName) (*ColName, SetScope, string, error) {
}
}

func isUserVar(part string) bool {
return len(part) >= 2 && part[0] == '@' && part[1] != '@'
}

// hasValidVarParts checks that `@`, `'`, and `"` does not prefix any name part
func hasValidVarParts(parts ...string) bool {
for _, part := range parts {
if strings.HasPrefix(part, `@`) ||
strings.HasPrefix(part, `'`) ||
strings.HasPrefix(part, `"`) {
return false
}
}
return true
}

// VarScope returns the SetScope of the given name, broken into parts. For example, `@@GLOBAL.sys_var` would become
// `[]string{"@@GLOBAL", "sys_var"}`. Returns the variable name without any scope specifiers, so the aforementioned
// variable would simply return "sys_var". `[]string{"@@other_var"}` would return "other_var". If a scope is not
Expand All @@ -6867,63 +6883,51 @@ func VarScope(nameParts ...string) (string, SetScope, string, error) {
// First case covers `@@@`, `@@@@`, etc.
if strings.HasPrefix(nameParts[0], "@@@") {
return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[0])
} else if strings.HasPrefix(nameParts[0], "@@") {
}
if strings.HasPrefix(nameParts[0], "@@") {
dotIdx := strings.Index(nameParts[0], ".")
if dotIdx != -1 {
return VarScope(nameParts[0][:dotIdx], nameParts[0][dotIdx+1:])
}
// Session scope is inferred here, but not explicitly requested
return trimQuotes(nameParts[0][2:]), SetScope_Session, "", nil
} else if strings.HasPrefix(nameParts[0], "@") {
}
if strings.HasPrefix(nameParts[0], "@") {
varName := nameParts[0][1:]
if len(varName) > 0 {
varName = trimQuotes(varName)
}
return varName, SetScope_User, "", nil
} else {
return nameParts[0], SetScope_None, "", nil
}
return nameParts[0], SetScope_None, "", nil
case 2:
// `@user.var` is valid, so we check for it here.
if len(nameParts[0]) >= 2 &&
nameParts[0][0] == '@' &&
nameParts[0][1] != '@' &&
!strings.HasPrefix(nameParts[1], "@") { // `@user.@var` is invalid though.
if isUserVar(nameParts[0]) {
if !hasValidVarParts(nameParts[1]) {
// Last value is column name, so we return that in the error
return "", SetScope_None, "", fmt.Errorf("invalid user variable declaration `%s`", nameParts[1])
}
// Last value is column name, so we return that in the error
return fmt.Sprintf("%s.%s", nameParts[0][1:], nameParts[1]), SetScope_User, "", nil
}
// We don't support variables such as `@@validate_password.length` right now, only `@@GLOBAL.sys_var`, etc.
// The `@` symbols are only valid on the first name_part. First case also catches `@@@`, etc.
if strings.HasPrefix(nameParts[1], "@@") {
if !hasValidVarParts(nameParts[1]) {
// Last value is column name, so we return that in the error
return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[1])
} else if strings.HasPrefix(nameParts[1], "@") {
return "", SetScope_None, "", fmt.Errorf("invalid user variable declaration `%s`", nameParts[1])
}
var setScope SetScope
switch strings.ToLower(nameParts[0]) {
case "@@global":
if strings.HasPrefix(nameParts[1], `"`) || strings.HasPrefix(nameParts[1], `'`) {
return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[1])
}
return trimQuotes(nameParts[1]), SetScope_Global, nameParts[0][2:], nil
setScope = SetScope_Global
case "@@persist":
if strings.HasPrefix(nameParts[1], `"`) || strings.HasPrefix(nameParts[1], `'`) {
return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[1])
}
return trimQuotes(nameParts[1]), SetScope_Persist, nameParts[0][2:], nil
setScope = SetScope_Persist
case "@@persist_only":
if strings.HasPrefix(nameParts[1], `"`) || strings.HasPrefix(nameParts[1], `'`) {
return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[1])
}
return trimQuotes(nameParts[1]), SetScope_PersistOnly, nameParts[0][2:], nil
setScope = SetScope_PersistOnly
case "@@session":
if strings.HasPrefix(nameParts[1], `"`) || strings.HasPrefix(nameParts[1], `'`) {
return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[1])
}
return trimQuotes(nameParts[1]), SetScope_Session, nameParts[0][2:], nil
setScope = SetScope_Session
case "@@local":
if strings.HasPrefix(nameParts[1], `"`) || strings.HasPrefix(nameParts[1], `'`) {
return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[1])
}
return trimQuotes(nameParts[1]), SetScope_Session, nameParts[0][2:], nil
setScope = SetScope_Session
case "@@validate_password":
return trimQuotes(fmt.Sprintf("%s.%s", nameParts[0][2:], nameParts[1])), SetScope_Global, "global", nil
default:
// This catches `@@@GLOBAL.sys_var`. Due to the earlier check, this does not error on `@user.var`.
if strings.HasPrefix(nameParts[0], "@") {
Expand All @@ -6932,25 +6936,36 @@ func VarScope(nameParts ...string) (string, SetScope, string, error) {
}
return nameParts[1], SetScope_None, "", nil
}
default:
return trimQuotes(nameParts[1]), setScope, nameParts[0][2:], nil
case 3:
// `@user.var.name` is valid, so we check for it here.
if len(nameParts[0]) >= 2 && nameParts[0][0] == '@' && nameParts[0][1] != '@' {
// `@` may only appear in the first name part for user variables
for i := 1; i < len(nameParts); i++ {
if strings.HasPrefix(nameParts[i], "@") {
// Last value is column name, so we return that in the error
return "", SetScope_None, "", fmt.Errorf("invalid user variable declaration `%s`", nameParts[len(nameParts)-1])
}
if isUserVar(nameParts[0]) {
if !hasValidVarParts(nameParts[1:]...) {
// Last value is column name, so we return that in the error
return "", SetScope_None, "", fmt.Errorf("invalid user variable declaration `%s`", nameParts[len(nameParts)-1])
}
return strings.Join(append([]string{nameParts[0][1:]}, nameParts[1:]...), "."), SetScope_User, "", nil
return fmt.Sprintf("%s.%s.%s", nameParts[0][1:], nameParts[1], nameParts[2]), SetScope_User, "", nil
}
if !hasValidVarParts(nameParts[1:]...) {
// Last value is column name, so we return that in the error
return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[len(nameParts)-1])
}
if strings.EqualFold(nameParts[0], "@@global") && strings.EqualFold(nameParts[1], "validate_password") {
return trimQuotes(fmt.Sprintf("%s.%s", nameParts[1], nameParts[2])), SetScope_Global, "global", nil
}
// As we don't support `@@GLOBAL.validate_password.length` or anything potentially longer, we error if any part
// starts with either `@@` or `@`. We can just check for `@` though.
for _, namePart := range nameParts {
if strings.HasPrefix(namePart, "@") {
return nameParts[len(nameParts)-1], SetScope_None, "", nil
default:
// `@user.var.name.xyz...` is valid, so we check for it here.
if isUserVar(nameParts[0]) {
if !hasValidVarParts(nameParts[1:]...) {
// Last value is column name, so we return that in the error
return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[len(nameParts)-1])
return "", SetScope_None, "", fmt.Errorf("invalid user variable declaration `%s`", nameParts[len(nameParts)-1])
}
return strings.Join(append([]string{nameParts[0][1:]}, nameParts[1:]...), "."), SetScope_User, "", nil
}
if !hasValidVarParts(nameParts[1:]...) {
// Last value is column name, so we return that in the error
return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[len(nameParts)-1])
}
return nameParts[len(nameParts)-1], SetScope_None, "", nil
}
Expand Down
14 changes: 9 additions & 5 deletions go/vt/sqlparser/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4215,6 +4215,13 @@ var (
input: "select * from (values row(date '2020-10-01', time '12:34:56', timestamp '2001-02-03 12:34:56')) t;",
output: "select * from (values row('2020-10-01', '12:34:56', '2001-02-03 12:34:56')) as t",
},
{
input: "set @@global.validate_password.length = 1",
output: "set global validate_password.length = 1",
},
{
input: "set @@session.validate_password.length = 1",
},
}

// Any tests that contain multiple statements within the body (such as BEGIN/END blocks) should go here.
Expand Down Expand Up @@ -7775,10 +7782,7 @@ var (
output: "syntax error at position 16 near '@@session.'",
}, {
input: "set xyz.@autocommit = true",
output: "invalid user variable declaration `@autocommit` at position 27 near 'true'",
}, {
input: "set @@session.validate_password.length = 1",
output: "invalid system variable declaration `length` at position 43 near '1'",
output: "invalid system variable declaration `@autocommit` at position 27 near 'true'",
}, {
input: "set session.@@validate_password.length = 1",
output: "invalid system variable declaration `@@validate_password.length` at position 43 near '1'",
Expand Down Expand Up @@ -7808,7 +7812,7 @@ var (
output: "invalid system variable declaration `@@autocommit` at position 38 near 'true'",
}, {
input: "set session other.@autocommit = true",
output: "invalid user variable declaration `@autocommit` at position 37 near 'true'",
output: "invalid system variable declaration `@autocommit` at position 37 near 'true'",
}, {
input: "select * from foo limit -100",
output: "syntax error at position 26 near 'limit'",
Expand Down