From 02bfd08a1168a6b03c5dbe225b081dc38d7c5271 Mon Sep 17 00:00:00 2001 From: James Cor Date: Thu, 13 Jun 2024 02:14:30 -0700 Subject: [PATCH 1/3] allow backticks --- go/vt/sqlparser/ast.go | 22 +++--- go/vt/sqlparser/parse_test.go | 124 ++++++++++++++++++++++++++-------- go/vt/sqlparser/token.go | 17 ++++- 3 files changed, 127 insertions(+), 36 deletions(-) diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index fb828d507a0..745f54b996a 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -6842,15 +6842,21 @@ func VarScope(nameParts ...string) (string, SetScope, string, error) { return VarScope(nameParts[0][:dotIdx], nameParts[0][dotIdx+1:]) } // Session scope is inferred here, but not explicitly requested - return nameParts[0][2:], SetScope_Session, "", nil + return trimQuotes(nameParts[0][2:]), SetScope_Session, "", nil } else if strings.HasPrefix(nameParts[0], "@") { - return nameParts[0][1:], SetScope_User, "", nil + varName := nameParts[0][1:] + if len(varName) > 0 { + varName = trimQuotes(varName) + } + return varName, SetScope_User, "", nil } else { 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] != '@' && + if len(nameParts[0]) >= 2 && + nameParts[0][0] == '@' && + nameParts[0][1] != '@' && !strings.HasPrefix(nameParts[1], "@") { // `@user.@var` is invalid though. return fmt.Sprintf("%s.%s", nameParts[0][1:], nameParts[1]), SetScope_User, "", nil } @@ -6866,27 +6872,27 @@ func VarScope(nameParts ...string) (string, SetScope, string, error) { if strings.HasPrefix(nameParts[1], `"`) || strings.HasPrefix(nameParts[1], `'`) { return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[1]) } - return nameParts[1], SetScope_Global, nameParts[0][2:], nil + return trimQuotes(nameParts[1]), SetScope_Global, nameParts[0][2:], nil case "@@persist": if strings.HasPrefix(nameParts[1], `"`) || strings.HasPrefix(nameParts[1], `'`) { return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[1]) } - return nameParts[1], SetScope_Persist, nameParts[0][2:], nil + return trimQuotes(nameParts[1]), SetScope_Persist, nameParts[0][2:], nil 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 nameParts[1], SetScope_PersistOnly, nameParts[0][2:], nil + return trimQuotes(nameParts[1]), SetScope_PersistOnly, nameParts[0][2:], nil case "@@session": if strings.HasPrefix(nameParts[1], `"`) || strings.HasPrefix(nameParts[1], `'`) { return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[1]) } - return nameParts[1], SetScope_Session, nameParts[0][2:], nil + return trimQuotes(nameParts[1]), SetScope_Session, nameParts[0][2:], nil case "@@local": if strings.HasPrefix(nameParts[1], `"`) || strings.HasPrefix(nameParts[1], `'`) { return "", SetScope_None, "", fmt.Errorf("invalid system variable declaration `%s`", nameParts[1]) } - return nameParts[1], SetScope_Session, nameParts[0][2:], nil + return trimQuotes(nameParts[1]), SetScope_Session, nameParts[0][2:], nil default: // This catches `@@@GLOBAL.sys_var`. Due to the earlier check, this does not error on `@user.var`. if strings.HasPrefix(nameParts[0], "@") { diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index 548b8eb2348..e730058cf10 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -1406,76 +1406,146 @@ var ( input: "set #simple\n b = 4", }, { input: "set character_set_results = utf8", - }, { + }, + { + input: "set @@`version` = true", + output: "set session version = true", + }, + { + input: "select @@`version` = true", + output: "select @@`version` = true", + }, + { input: "set @@session.autocommit = true", output: "set session autocommit = true", - }, { + }, + { input: "set @@session.`autocommit` = true", - output: "set session `autocommit` = true", - }, { + output: "set session autocommit = true", + }, + { + input: "select @@session.`autocommit` = true", + output: "select @@session.`autocommit` = true", + }, + { input: "set @@session.autocommit = ON", output: "set session autocommit = 'ON'", - }, { + }, + { input: "set @@session.autocommit= OFF", output: "set session autocommit = 'OFF'", - }, { + }, + { input: "set session autocommit = ON", output: "set session autocommit = 'ON'", - }, { + }, + { input: "set session autocommit := ON", output: "set session autocommit = 'ON'", - }, { + }, + { input: "set global autocommit = OFF", output: "set global autocommit = 'OFF'", - }, { + }, + { input: "set @@global.optimizer_prune_level = 1", output: "set global optimizer_prune_level = 1", - }, { + }, + { input: "set global optimizer_prune_level = 1", - }, { + }, + { input: "set @@persist.optimizer_prune_level = 1", output: "set persist optimizer_prune_level = 1", - }, { + }, + { input: "set persist optimizer_prune_level = 1", - }, { + }, + { input: "set @@persist_only.optimizer_prune_level = 1", output: "set persist_only optimizer_prune_level = 1", - }, { + }, + { input: "set persist_only optimizer_prune_level = 1", - }, { + }, + { input: "set @@local.optimizer_prune_level = 1", output: "set session optimizer_prune_level = 1", - }, { + }, + { input: "set local optimizer_prune_level = 1", output: "set session optimizer_prune_level = 1", - }, { + }, + { input: "set @@optimizer_prune_level = 1", output: "set session optimizer_prune_level = 1", - }, { + }, + { input: "set session optimizer_prune_level = 1", - }, { + }, + { input: "set @@optimizer_prune_level = 1, @@global.optimizer_search_depth = 62", output: "set session optimizer_prune_level = 1, global optimizer_search_depth = 62", - }, { + }, + { input: "set @@GlObAl.optimizer_prune_level = 1", output: "set global optimizer_prune_level = 1", - }, { + }, + { input: "set @user.var = 1", - }, { + }, + { input: "set @user.var.name = 1", - }, { + }, + { input: "set @user.var.name := 1", output: "set @user.var.name = 1", - }, { + }, + { + input: "set @`user var` = 1", + output: "set @user var = 1", + }, + { + input: "select @`user var`", + output: "select `@``user var```", + }, + { + input: "set @user.`var` = 1", + output: "set @user.var = 1", + }, + { + input: "select @user.`var`", + output: "select @user.var", + }, + { + input: "set @`user`.`var` = 1", + output: "set @`user`.var = 1", + }, + { + input: "select @`user`.`var`", + output: "select `@``user```.var", + }, + { + input: "set @abc.def.`ghi` = 300", + output: "set @abc.def.ghi = 300", + }, + { + input: "select @abc.def.`ghi`", + output: "select @abc.def.ghi", + }, + { input: "set autocommit = on", output: "set autocommit = 'on'", - }, { + }, + { input: "set autocommit = off", output: "set autocommit = 'off'", - }, { + }, + { input: "set autocommit = off, foo = 1", output: "set autocommit = 'off', foo = 1", - }, { + }, + { input: "set names utf8 collate foo", output: "set names 'utf8'", }, { diff --git a/go/vt/sqlparser/token.go b/go/vt/sqlparser/token.go index b57fa8a76c9..0dec4efcc8e 100644 --- a/go/vt/sqlparser/token.go +++ b/go/vt/sqlparser/token.go @@ -428,10 +428,25 @@ func (tkn *Tokenizer) scanIdentifier(firstByte byte, isDbSystemVariable bool) (i buffer.WriteByte(byte(tkn.lastChar)) tkn.next() } - for isLetter(tkn.lastChar) || isDigit(tkn.lastChar) || (isDbSystemVariable && isCarat(tkn.lastChar)) { + for isLetter(tkn.lastChar) || isDigit(tkn.lastChar) || (isDbSystemVariable && isCarat(tkn.lastChar)) /*|| (firstByte == '@' && isCarat(tkn.lastChar))*/ { buffer.WriteByte(byte(tkn.lastChar)) tkn.next() } + + // special case for user variables with backticks + if firstByte == '@' && tkn.lastChar == '`' { + buffer.WriteByte(byte(tkn.lastChar)) + tkn.next() + for isLetter(tkn.lastChar) || isDigit(tkn.lastChar) || isCarat(tkn.lastChar) || unicode.IsSpace(rune(tkn.lastChar)) { + buffer.WriteByte(byte(tkn.lastChar)) + if tkn.lastChar == '`' { + tkn.next() + break + } + tkn.next() + } + } + if tkn.lastChar == '@' { tkn.potentialAccountName = true } From 37eaada8e2ee09a33931f8b841416c2ebea7b1b3 Mon Sep 17 00:00:00 2001 From: James Cor Date: Thu, 13 Jun 2024 11:34:35 -0700 Subject: [PATCH 2/3] fix formatter and add helper method --- go/vt/sqlparser/ast.go | 7 ++++++- go/vt/sqlparser/parse_test.go | 18 ++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index 745f54b996a..beed422945b 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -7225,8 +7225,13 @@ func formatID(buf *TrackedBuffer, original, lowered string) { isDbSystemVariable = true } + isUserVariable := false + if !isDbSystemVariable && len(original) > 0 && original[:1] == "@" { + isUserVariable = true + } + for i, c := range original { - if !(isLetter(uint16(c)) || c == '@') && (!isDbSystemVariable || !isCarat(uint16(c))) { + if !(isLetter(uint16(c)) || c == '@') && (!isDbSystemVariable || !isCarat(uint16(c))) && !isUserVariable { if i == 0 || !isDigit(uint16(c)) { goto mustEscape } diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index e730058cf10..03414f6f4ea 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -1507,7 +1507,7 @@ var ( }, { input: "select @`user var`", - output: "select `@``user var```", + output: "select @`user var`", }, { input: "set @user.`var` = 1", @@ -1523,7 +1523,7 @@ var ( }, { input: "select @`user`.`var`", - output: "select `@``user```.var", + output: "select @`user`.var", }, { input: "set @abc.def.`ghi` = 300", @@ -4471,6 +4471,20 @@ end`, } ) +// TestSingleSQL is a helper function to test a single SQL statement. +func TestSingleSQL(t *testing.T) { + t.Skip() + tests := []parseTest{ + { + input: "select @`user var`", + output: "select @`user var`", + }, + } + for _, tcase := range tests { + runParseTestCase(t, tcase) + } +} + func TestValid(t *testing.T) { validSQL = append(validSQL, validMultiStatementSql...) for _, tcase := range validSQL { From 5566728a9c0e9d8f0dd4f1b80833087b603978e4 Mon Sep 17 00:00:00 2001 From: James Cor Date: Thu, 13 Jun 2024 11:47:26 -0700 Subject: [PATCH 3/3] comment --- go/vt/sqlparser/token.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/vt/sqlparser/token.go b/go/vt/sqlparser/token.go index 0dec4efcc8e..6be9ade4b30 100644 --- a/go/vt/sqlparser/token.go +++ b/go/vt/sqlparser/token.go @@ -428,7 +428,7 @@ func (tkn *Tokenizer) scanIdentifier(firstByte byte, isDbSystemVariable bool) (i buffer.WriteByte(byte(tkn.lastChar)) tkn.next() } - for isLetter(tkn.lastChar) || isDigit(tkn.lastChar) || (isDbSystemVariable && isCarat(tkn.lastChar)) /*|| (firstByte == '@' && isCarat(tkn.lastChar))*/ { + for isLetter(tkn.lastChar) || isDigit(tkn.lastChar) || (isDbSystemVariable && isCarat(tkn.lastChar)) { buffer.WriteByte(byte(tkn.lastChar)) tkn.next() }