-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix known mysql type conversion issues (#6647)
(cherry picked from commit d858d82)
- Loading branch information
1 parent
c6f6c2a
commit 29dff92
Showing
3 changed files
with
216 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
}) | ||
} | ||
} |