1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,178 @@
package v2
import (
"bytes"
"database/sql"
"errors"
"fmt"
"strconv"
)
type conversionFunc func(value sql.RawBytes) (interface{}, error)
// ParseInt parses the given sql.RawBytes value into an int64.
// It returns the parsed value and an error if the parsing fails.
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."
var numErr *strconv.NumError
if errors.As(err, &numErr) && errors.Is(numErr, strconv.ErrRange) {
return v, nil
}
return v, err
}
// ParseUint parses the given sql.RawBytes value into an uint64.
// It returns the parsed value and an error if the parsing fails.
func ParseUint(value sql.RawBytes) (interface{}, error) {
return strconv.ParseUint(string(value), 10, 64)
}
// ParseFloat parses the given sql.RawBytes value into a float64.
// It returns the parsed value and an error if the parsing fails.
func ParseFloat(value sql.RawBytes) (interface{}, error) {
return strconv.ParseFloat(string(value), 64)
}
// ParseBoolAsInteger parses the given sql.RawBytes value into an int64
// representing a boolean value. It returns 1 for "YES" or "ON" and 0 otherwise.
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
}
// ParseString parses the given sql.RawBytes value into a string.
// It returns the parsed value and an error if the parsing fails.
func ParseString(value sql.RawBytes) (interface{}, error) {
return string(value), nil
}
// ParseGTIDMode parses the given sql.RawBytes value into an int64
// representing the GTID mode. It returns an error if the value is unrecognized.
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)
}
}
// ParseValue attempts to parse the given sql.RawBytes value into an appropriate type.
// It returns the parsed value and an error if the parsing fails.
func ParseValue(value sql.RawBytes) (interface{}, error) {
if bytes.EqualFold(value, []byte("YES")) || bytes.Equal(value, []byte("ON")) {
return int64(1), nil
}
if bytes.EqualFold(value, []byte("NO")) || bytes.Equal(value, []byte("OFF")) {
return int64(0), nil
}
if val, err := strconv.ParseInt(string(value), 10, 64); err == nil {
return val, nil
}
if val, err := strconv.ParseUint(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{
"innodb_available_undo_logs": ParseUint,
"innodb_buffer_pool_pages_misc": ParseUint,
"innodb_data_pending_fsyncs": ParseUint,
"ssl_ctx_verify_depth": ParseUint,
"ssl_verify_depth": ParseUint,
// see https://galeracluster.com/library/documentation/galera-status-variables.html
"wsrep_apply_oooe": ParseFloat,
"wsrep_apply_oool": ParseFloat,
"wsrep_apply_window": ParseFloat,
"wsrep_cert_deps_distance": ParseFloat,
"wsrep_cert_interval": ParseFloat,
"wsrep_commit_oooe": ParseFloat,
"wsrep_commit_oool": ParseFloat,
"wsrep_commit_window": ParseFloat,
"wsrep_flow_control_paused": ParseFloat,
"wsrep_local_index": ParseUint,
"wsrep_local_recv_queue_avg": ParseFloat,
"wsrep_local_send_queue_avg": ParseFloat,
}
var globalVariableConversions = map[string]conversionFunc{
// see https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html
// see https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html
"delay_key_write": ParseString, // ON, OFF, ALL
"enforce_gtid_consistency": ParseString, // ON, OFF, WARN
"event_scheduler": ParseString, // YES, NO, DISABLED
"have_openssl": ParseBoolAsInteger, // alias for have_ssl
"have_ssl": ParseBoolAsInteger, // YES, DISABLED
"have_symlink": ParseBoolAsInteger, // YES, NO, DISABLED
"session_track_gtids": ParseString,
"session_track_transaction_info": ParseString,
"ssl_fips_mode": ParseString,
"use_secondary_engine": ParseString,
// https://dev.mysql.com/doc/refman/5.7/en/replication-options-binary-log.html
// https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-log.html
"transaction_write_set_extraction": ParseString,
// https://dev.mysql.com/doc/refman/5.7/en/replication-options-replica.html
// https://dev.mysql.com/doc/refman/8.0/en/replication-options-replica.html
"slave_skip_errors": ParseString,
// https://dev.mysql.com/doc/refman/5.7/en/replication-options-gtids.html
// https://dev.mysql.com/doc/refman/8.0/en/replication-options-gtids.html
"gtid_mode": ParseGTIDMode,
}
// ConvertGlobalStatus converts the given key and sql.RawBytes value into an appropriate type based on globalStatusConversions.
// It returns the converted value and an error if the conversion fails.
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)
}
// ConvertGlobalVariables converts the given key and sql.RawBytes value into an appropriate type based on globalVariableConversions.
// It returns the converted value and an error if the conversion fails.
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)
}

View file

@ -0,0 +1,127 @@
package v2
import (
"database/sql"
"strings"
"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: uint64(0),
expectedErr: nil,
},
{
name: "overflow int64",
key: "ssl_ctx_verify_depth",
value: []byte("18446744073709551615"),
expected: uint64(18446744073709551615),
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 TestConvertGlobalVariables(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)
})
}
}
func TestParseValue(t *testing.T) {
testCases := []struct {
rawByte sql.RawBytes
output interface{}
err string
}{
{sql.RawBytes("123"), int64(123), ""},
{sql.RawBytes("abc"), "abc", ""},
{sql.RawBytes("10.1"), 10.1, ""},
{sql.RawBytes("ON"), int64(1), ""},
{sql.RawBytes("OFF"), int64(0), ""},
{sql.RawBytes("NO"), int64(0), ""},
{sql.RawBytes("YES"), int64(1), ""},
{sql.RawBytes("No"), int64(0), ""},
{sql.RawBytes("Yes"), int64(1), ""},
{sql.RawBytes("-794"), int64(-794), ""},
{sql.RawBytes("2147483647"), int64(2147483647), ""}, // max int32
{sql.RawBytes("2147483648"), int64(2147483648), ""}, // too big for int32
{sql.RawBytes("9223372036854775807"), int64(9223372036854775807), ""}, // max int64
{sql.RawBytes("9223372036854775808"), uint64(9223372036854775808), ""}, // too big for int64
{sql.RawBytes("18446744073709551615"), uint64(18446744073709551615), ""}, // max uint64
{sql.RawBytes("18446744073709551616"), float64(18446744073709552000), ""}, // too big for uint64
{sql.RawBytes("18446744073709552333"), float64(18446744073709552000), ""}, // too big for uint64
{sql.RawBytes(""), nil, "unconvertible value"},
}
for _, cases := range testCases {
got, err := ParseValue(cases.rawByte)
if err != nil && cases.err == "" {
t.Errorf("for %q got unexpected error: %q", string(cases.rawByte), err.Error())
} else if err != nil && !strings.HasPrefix(err.Error(), cases.err) {
t.Errorf("for %q wanted error %q, got %q", string(cases.rawByte), cases.err, err.Error())
} else if err == nil && cases.err != "" {
t.Errorf("for %q did not get expected error: %s", string(cases.rawByte), cases.err)
} else if got != cases.output {
t.Errorf("for %q wanted %#v (%T), got %#v (%T)", string(cases.rawByte), cases.output, cases.output, got, got)
}
}
}