179 lines
6.2 KiB
Go
179 lines
6.2 KiB
Go
|
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)
|
||
|
}
|