Skip to content
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
53 changes: 52 additions & 1 deletion go/vt/servenv/buildinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ import (
"strconv"
"time"

"vitess.io/vitess/go/vt/proto/vtrpc"
"vitess.io/vitess/go/vt/vterrors"

"vitess.io/vitess/go/vt/log"

"vitess.io/vitess/go/vt/sqlparser"

"vitess.io/vitess/go/stats"
)

Expand Down Expand Up @@ -104,7 +111,13 @@ func init() {
goArch: runtime.GOARCH,
version: versionName,
}

var convVersion string
convVersion, err = convertMySQLVersionToCommentVersion(AppVersion.MySQLVersion())
if err != nil {
log.Error(err)
} else {
sqlparser.MySQLVersion = convVersion
}
stats.NewString("BuildHost").Set(AppVersion.buildHost)
stats.NewString("BuildUser").Set(AppVersion.buildUser)
stats.NewGauge("BuildTimestamp", "build timestamp").Set(AppVersion.buildTime)
Expand All @@ -126,3 +139,41 @@ func init() {
}
stats.NewGaugesWithMultiLabels("BuildInformation", "build information exposed via label", buildLabels).Set(buildValues, 1)
}

// convertMySQLVersionToCommentVersion converts the MySQL version into comment version format.
func convertMySQLVersionToCommentVersion(version string) (string, error) {
var res = make([]int, 3)
idx := 0
val := ""
for _, c := range version {
if c <= '9' && c >= '0' {
val += string(c)
} else if c == '.' {
v, err := strconv.Atoi(val)
if err != nil {
return "", err
}
val = ""
res[idx] = v
idx++
if idx == 3 {
break
}
} else {
break
}
}
if val != "" {
v, err := strconv.Atoi(val)
if err != nil {
return "", err
}
res[idx] = v
idx++
}
if idx == 0 {
return "", vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "MySQL version not correctly setup - %s.", version)
}

return fmt.Sprintf("%01d%02d%02d", res[0], res[1], res[2]), nil
}
40 changes: 40 additions & 0 deletions go/vt/servenv/buildinfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"testing"
"time"

"github.com/stretchr/testify/require"

"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -50,3 +52,41 @@ func TestVersionString(t *testing.T) {
MySQLServerVersion = &newVersion
assert.Equal(t, newVersion, v.MySQLVersion())
}

func TestConvertMySQLVersion(t *testing.T) {
testcases := []struct {
version string
commentVersion string
error string
}{{
version: "5.7.9",
commentVersion: "50709",
}, {
version: "0008.08.9",
commentVersion: "80809",
}, {
version: "5.7.9, Vitess - 10.0.1",
commentVersion: "50709",
}, {
version: "8.1 Vitess - 10.0.1",
commentVersion: "80100",
}, {
version: "Vitess - 10.0.1",
error: "MySQL version not correctly setup - Vitess - 10.0.1.",
}, {
version: "5.7.9.22",
commentVersion: "50709",
}}

for _, tcase := range testcases {
t.Run(tcase.version, func(t *testing.T) {
output, err := convertMySQLVersionToCommentVersion(tcase.version)
if tcase.error != "" {
require.EqualError(t, err, tcase.error)
} else {
require.NoError(t, err)
require.Equal(t, tcase.commentVersion, output)
}
})
}
}
3 changes: 3 additions & 0 deletions go/vt/sqlparser/comments.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ func ExtractMysqlComment(sql string) (string, string) {
if endOfVersionIndex < 0 {
return "", ""
}
if endOfVersionIndex < 5 {
endOfVersionIndex = 0
}
version := sql[0:endOfVersionIndex]
innerSQL := strings.TrimFunc(sql[endOfVersionIndex:], unicode.IsSpace)

Expand Down
3 changes: 3 additions & 0 deletions go/vt/sqlparser/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1762,6 +1762,9 @@ var (
input: "create database test_db character set * unparsable",
output: "create database test_db",
partialDDL: true,
}, {
input: "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysql` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;",
output: "create database if not exists mysql default character set utf8mb4 collate utf8mb4_0900_ai_ci",
}, {
input: "drop database test_db",
}, {
Expand Down
3 changes: 3 additions & 0 deletions go/vt/sqlparser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ var parserPool = sync.Pool{}
// zeroParser is a zero-initialized parser to help reinitialize the parser for pooling.
var zeroParser = *(yyNewParser().(*yyParserImpl))

// MySQLVersion is the version of MySQL that the parser would emulate
var MySQLVersion string = "50709"

// yyParsePooled is a wrapper around yyParse that pools the parser objects. There isn't a
// particularly good reason to use yyParse directly, since it immediately discards its parser. What
// would be ideal down the line is to actually pool the stacks themselves rather than the parser
Expand Down
10 changes: 8 additions & 2 deletions go/vt/sqlparser/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -1027,8 +1027,14 @@ func (tkn *Tokenizer) scanMySQLSpecificComment() (int, []byte) {
}
tkn.consumeNext(buffer)
}
_, sql := ExtractMysqlComment(buffer.String())
tkn.specialComment = NewStringTokenizer(sql)

commentVersion, sql := ExtractMysqlComment(buffer.String())

if MySQLVersion >= commentVersion {
// Only add the special comment to the tokenizer if the version of MySQL is higher or equal to the comment version
tkn.specialComment = NewStringTokenizer(sql)
}

return tkn.Scan()
}

Expand Down
74 changes: 67 additions & 7 deletions go/vt/sqlparser/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package sqlparser
import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
)

func TestLiteralID(t *testing.T) {
Expand Down Expand Up @@ -75,9 +77,8 @@ func TestLiteralID(t *testing.T) {
for _, tcase := range testcases {
tkn := NewStringTokenizer(tcase.in)
id, out := tkn.Scan()
if tcase.id != id || string(out) != tcase.out {
t.Errorf("Scan(%s): %d, %s, want %d, %s", tcase.in, id, out, tcase.id, tcase.out)
}
require.Equal(t, tcase.id, id)
require.Equal(t, tcase.out, string(out))
}
}

Expand Down Expand Up @@ -146,10 +147,11 @@ func TestString(t *testing.T) {
}}

for _, tcase := range testcases {
id, got := NewStringTokenizer(tcase.in).Scan()
if tcase.id != id || string(got) != tcase.want {
t.Errorf("Scan(%q) = (%s, %q), want (%s, %q)", tcase.in, tokenName(id), got, tokenName(tcase.id), tcase.want)
}
t.Run(tcase.in, func(t *testing.T) {
id, got := NewStringTokenizer(tcase.in).Scan()
require.Equal(t, tcase.id, id, "Scan(%q) = (%s), want (%s)", tcase.in, tokenName(id), tokenName(tcase.id))
require.Equal(t, tcase.want, string(got))
})
}
}

Expand Down Expand Up @@ -205,3 +207,61 @@ func TestSplitStatement(t *testing.T) {
}
}
}

func TestVersion(t *testing.T) {
testcases := []struct {
version string
in string
id []int
}{{
version: "50709",
in: "/*!80102 SELECT*/ FROM IN EXISTS",
id: []int{FROM, IN, EXISTS, 0},
}, {
version: "80101",
in: "/*!80102 SELECT*/ FROM IN EXISTS",
id: []int{FROM, IN, EXISTS, 0},
}, {
version: "80201",
in: "/*!80102 SELECT*/ FROM IN EXISTS",
id: []int{SELECT, FROM, IN, EXISTS, 0},
}, {
version: "80102",
in: "/*!80102 SELECT*/ FROM IN EXISTS",
id: []int{SELECT, FROM, IN, EXISTS, 0},
}}

for _, tcase := range testcases {
t.Run(tcase.version+"_"+tcase.in, func(t *testing.T) {
MySQLVersion = tcase.version
tok := NewStringTokenizer(tcase.in)
for _, expectedID := range tcase.id {
id, _ := tok.Scan()
require.Equal(t, expectedID, id)
}
})
}
}

func TestExtractMySQLComment(t *testing.T) {
testcases := []struct {
comment string
version string
}{{
comment: "/*!50108 SELECT * FROM */",
version: "50108",
}, {
comment: "/*!5018 SELECT * FROM */",
version: "",
}, {
comment: "/*!SELECT * FROM */",
version: "",
}}

for _, tcase := range testcases {
t.Run(tcase.version, func(t *testing.T) {
output, _ := ExtractMysqlComment(tcase.comment)
require.Equal(t, tcase.version, output)
})
}
}