Fix known mysql type conversion issues (#6647)
This commit is contained in:
103
plugins/inputs/mysql/v2/convert.go
Normal file
103
plugins/inputs/mysql/v2/convert.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type ConversionFunc func(value sql.RawBytes) (interface{}, error)
|
||||
|
||||
func ParseInt(value sql.RawBytes) (interface{}, error) {
|
||||
v, err := strconv.ParseInt(string(value), 10, 64)
|
||||
|
||||
// Ignore ErrRange. When this error is set the returned value is "the
|
||||
// maximum magnitude integer of the appropriate bitSize and sign."
|
||||
if err, ok := err.(*strconv.NumError); ok && err.Err == strconv.ErrRange {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
return v, err
|
||||
}
|
||||
|
||||
func ParseBoolAsInteger(value sql.RawBytes) (interface{}, error) {
|
||||
if bytes.EqualFold(value, []byte("YES")) || bytes.EqualFold(value, []byte("ON")) {
|
||||
return int64(1), nil
|
||||
}
|
||||
|
||||
return int64(0), nil
|
||||
}
|
||||
|
||||
func ParseGTIDMode(value sql.RawBytes) (interface{}, error) {
|
||||
// https://dev.mysql.com/doc/refman/8.0/en/replication-mode-change-online-concepts.html
|
||||
v := string(value)
|
||||
switch v {
|
||||
case "OFF":
|
||||
return int64(0), nil
|
||||
case "ON":
|
||||
return int64(1), nil
|
||||
case "OFF_PERMISSIVE":
|
||||
return int64(0), nil
|
||||
case "ON_PERMISSIVE":
|
||||
return int64(1), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized gtid_mode: %q", v)
|
||||
}
|
||||
}
|
||||
|
||||
func ParseValue(value sql.RawBytes) (interface{}, error) {
|
||||
if bytes.EqualFold(value, []byte("YES")) || bytes.Compare(value, []byte("ON")) == 0 {
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
if bytes.EqualFold(value, []byte("NO")) || bytes.Compare(value, []byte("OFF")) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if val, err := strconv.ParseInt(string(value), 10, 64); err == nil {
|
||||
return val, nil
|
||||
}
|
||||
if val, err := strconv.ParseFloat(string(value), 64); err == nil {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
if len(string(value)) > 0 {
|
||||
return string(value), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unconvertible value: %q", string(value))
|
||||
}
|
||||
|
||||
var GlobalStatusConversions = map[string]ConversionFunc{
|
||||
"ssl_ctx_verify_depth": ParseInt,
|
||||
"ssl_verify_depth": ParseInt,
|
||||
}
|
||||
|
||||
var GlobalVariableConversions = map[string]ConversionFunc{
|
||||
"gtid_mode": ParseGTIDMode,
|
||||
}
|
||||
|
||||
func ConvertGlobalStatus(key string, value sql.RawBytes) (interface{}, error) {
|
||||
if bytes.Equal(value, []byte("")) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if conv, ok := GlobalStatusConversions[key]; ok {
|
||||
return conv(value)
|
||||
}
|
||||
|
||||
return ParseValue(value)
|
||||
}
|
||||
|
||||
func ConvertGlobalVariables(key string, value sql.RawBytes) (interface{}, error) {
|
||||
if bytes.Equal(value, []byte("")) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if conv, ok := GlobalVariableConversions[key]; ok {
|
||||
return conv(value)
|
||||
}
|
||||
|
||||
return ParseValue(value)
|
||||
}
|
||||
86
plugins/inputs/mysql/v2/convert_test.go
Normal file
86
plugins/inputs/mysql/v2/convert_test.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConvertGlobalStatus(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key string
|
||||
value sql.RawBytes
|
||||
expected interface{}
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
key: "ssl_ctx_verify_depth",
|
||||
value: []byte("0"),
|
||||
expected: int64(0),
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "overflow int64",
|
||||
key: "ssl_ctx_verify_depth",
|
||||
value: []byte("18446744073709551615"),
|
||||
expected: int64(9223372036854775807),
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "defined variable but unset",
|
||||
key: "ssl_ctx_verify_depth",
|
||||
value: []byte(""),
|
||||
expected: nil,
|
||||
expectedErr: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual, err := ConvertGlobalStatus(tt.key, tt.value)
|
||||
require.Equal(t, tt.expectedErr, err)
|
||||
require.Equal(t, tt.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCovertGlobalVariables(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key string
|
||||
value sql.RawBytes
|
||||
expected interface{}
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "boolean type mysql<=5.6",
|
||||
key: "gtid_mode",
|
||||
value: []byte("ON"),
|
||||
expected: int64(1),
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "enum type mysql>=5.7",
|
||||
key: "gtid_mode",
|
||||
value: []byte("ON_PERMISSIVE"),
|
||||
expected: int64(1),
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "defined variable but unset",
|
||||
key: "ssl_ctx_verify_depth",
|
||||
value: []byte(""),
|
||||
expected: nil,
|
||||
expectedErr: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual, err := ConvertGlobalVariables(tt.key, tt.value)
|
||||
require.Equal(t, tt.expectedErr, err)
|
||||
require.Equal(t, tt.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user