// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package parser_driver

import (
	"fmt"
	"strconv"

	"github.com/pingcap/errors"
	"github.com/abbychau/mysql-parser/parser_driver/mysql"
)

// UnspecifiedLength is unspecified length.
const UnspecifiedLength = -1

// ErrorLength is error length for blob or text.
const ErrorLength = 0

// FieldType records field type information.
type FieldType struct {
	tp      byte
	flag    uint
	flen    int
	decimal int
	charset string
	collate string
	elems   []string
}

// GetType returns the type
func (ft *FieldType) GetType() byte {
	return ft.tp
}

// SetType sets the type
func (ft *FieldType) SetType(tp byte) {
	ft.tp = tp
}

// GetFlag returns the flag
func (ft *FieldType) GetFlag() uint {
	return ft.flag
}

// SetFlag sets the flag
func (ft *FieldType) SetFlag(flag uint) {
	ft.flag = flag
}

// AddFlag adds flag
func (ft *FieldType) AddFlag(flag uint) {
	ft.flag |= flag
}

// DelFlag removes flag
func (ft *FieldType) DelFlag(flag uint) {
	ft.flag &= ^flag
}

// GetFlen returns the field length
func (ft *FieldType) GetFlen() int {
	return ft.flen
}

// SetFlen sets the field length
func (ft *FieldType) SetFlen(flen int) {
	ft.flen = flen
}

// SetFlenUnderLimit sets the field length under limit
func (ft *FieldType) SetFlenUnderLimit(flen int) {
	if flen > mysql.MaxDecimalWidth {
		ft.flen = mysql.MaxDecimalWidth
	} else {
		ft.flen = flen
	}
}

// GetDecimal returns the decimal
func (ft *FieldType) GetDecimal() int {
	return ft.decimal
}

// SetDecimal sets the decimal
func (ft *FieldType) SetDecimal(decimal int) {
	ft.decimal = decimal
}

// SetDecimalUnderLimit sets the decimal under limit
func (ft *FieldType) SetDecimalUnderLimit(decimal int) {
	if decimal > mysql.MaxDecimalScale {
		ft.decimal = mysql.MaxDecimalScale
	} else {
		ft.decimal = decimal
	}
}

// GetCharset returns the charset
func (ft *FieldType) GetCharset() string {
	return ft.charset
}

// SetCharset sets the charset
func (ft *FieldType) SetCharset(charset string) {
	ft.charset = charset
}

// GetCollate returns the collation
func (ft *FieldType) GetCollate() string {
	return ft.collate
}

// SetCollate sets the collation
func (ft *FieldType) SetCollate(collate string) {
	ft.collate = collate
}

// GetElems returns the elements for ENUM/SET
func (ft *FieldType) GetElems() []string {
	return ft.elems
}

// SetElems sets the elements for ENUM/SET
func (ft *FieldType) SetElems(elems []string) {
	ft.elems = elems
}

// EvalType returns the EvalType
func (ft *FieldType) EvalType() EvalType {
	switch ft.tp {
	case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeBit, mysql.TypeYear:
		return ETInt
	case mysql.TypeFloat, mysql.TypeDouble:
		return ETReal
	case mysql.TypeNewDecimal:
		return ETDecimal
	case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp:
		return ETDatetime
	case mysql.TypeDuration:
		return ETDuration
	case mysql.TypeJSON:
		return ETJson
	case mysql.TypeTiDBVectorFloat32:
		return ETVectorFloat32
	default:
		return ETString
	}
}

// CompactStr returns a compact string representation
func (ft *FieldType) CompactStr() string {
	return TypeStr(ft.tp)
}

// ToggleFlag toggles a flag on/off
func (ft *FieldType) ToggleFlag(flag uint) {
	ft.flag ^= flag
}

// SetArray sets whether this is an array type
func (ft *FieldType) SetArray(isArray bool) {
	// Stub implementation - arrays not fully supported yet
}

// NewFieldType returns a FieldType,
// with a type and other information about field type.
func NewFieldType(tp byte) *FieldType {
	charset1, collate1 := DefaultCharsetForType(tp)
	flen, decimal := minFlenAndDecimalForType(tp)
	return NewFieldTypeBuilder().
		SetType(tp).
		SetCharset(charset1).
		SetCollate(collate1).
		SetFlen(flen).
		SetDecimal(decimal).
		BuildP()
}

// NewFieldTypeWithCollation returns a FieldType,
// with a type and other information about field type.
func NewFieldTypeWithCollation(tp byte, collation string, length int) *FieldType {
	coll, _ := GetCollationByName(collation)
	return NewFieldTypeBuilder().SetType(tp).SetFlen(length).SetCharset(coll.CharsetName).SetCollate(collation).SetDecimal(UnspecifiedLength).BuildP()
}

// AggFieldType aggregates field types for a multi-argument function like `IF`, `IFNULL`, `COALESCE`
// whose return type is determined by the arguments' FieldTypes.
// Aggregation is performed by MergeFieldType function.
func AggFieldType(tps []*FieldType) *FieldType {
	var currType FieldType
	isMixedSign := false
	for i, t := range tps {
		if i == 0 && currType.GetType() == mysql.TypeUnspecified {
			currType = *t
			continue
		}
		mtp := mergeFieldType(currType.GetType(), t.GetType())
		isMixedSign = isMixedSign || (mysql.HasUnsignedFlag(currType.GetFlag()) != mysql.HasUnsignedFlag(t.GetFlag()))
		currType.SetType(mtp)
		currType.SetFlag(mergeTypeFlag(currType.GetFlag(), t.GetFlag()))
	}
	// integral promotion when tps contains signed and unsigned
	if isMixedSign && IsTypeInteger(currType.GetType()) {
		bumpRange := false // indicate one of tps bump currType range
		for _, t := range tps {
			bumpRange = bumpRange || (mysql.HasUnsignedFlag(t.GetFlag()) && (t.GetType() == currType.GetType() ||
				t.GetType() == mysql.TypeBit))
		}
		if bumpRange {
			switch currType.GetType() {
			case mysql.TypeTiny:
				currType.SetType(mysql.TypeShort)
			case mysql.TypeShort:
				currType.SetType(mysql.TypeInt24)
			case mysql.TypeInt24:
				currType.SetType(mysql.TypeLong)
			case mysql.TypeLong:
				currType.SetType(mysql.TypeLonglong)
			case mysql.TypeLonglong:
				currType.SetType(mysql.TypeNewDecimal)
			}
		}
	}

	if mysql.HasUnsignedFlag(currType.GetFlag()) && !isMixedSign {
		currType.AddFlag(mysql.UnsignedFlag)
	}

	return &currType
}

// TryToFixFlenOfDatetime try to fix flen of Datetime for specific func or other field merge cases
func TryToFixFlenOfDatetime(resultTp *FieldType) {
	if resultTp.GetType() == mysql.TypeDatetime {
		resultTp.SetFlen(mysql.MaxDatetimeWidthNoFsp)
		if resultTp.GetDecimal() > 0 {
			resultTp.SetFlen(resultTp.GetFlen() + resultTp.GetDecimal() + 1)
		}
	}
}

// AggregateEvalType aggregates arguments' EvalType of a multi-argument function.
func AggregateEvalType(fts []*FieldType, flag *uint) EvalType {
	var (
		aggregatedEvalType = ETString
		unsigned           bool
		gotFirst           bool
		gotBinString       bool
	)
	lft := fts[0]
	for _, ft := range fts {
		if ft.GetType() == mysql.TypeNull {
			continue
		}
		et := ft.EvalType()
		rft := ft
		if (IsTypeBlob(ft.GetType()) || IsTypeVarchar(ft.GetType()) || IsTypeChar(ft.GetType())) && mysql.HasBinaryFlag(ft.GetFlag()) {
			gotBinString = true
		}
		if !gotFirst {
			gotFirst = true
			aggregatedEvalType = et
			unsigned = mysql.HasUnsignedFlag(ft.GetFlag())
		} else {
			aggregatedEvalType = mergeEvalType(aggregatedEvalType, et, lft, rft, unsigned, mysql.HasUnsignedFlag(ft.GetFlag()))
			unsigned = unsigned && mysql.HasUnsignedFlag(ft.GetFlag())
		}
		lft = rft
	}
	SetTypeFlag(flag, mysql.UnsignedFlag, unsigned)
	SetTypeFlag(flag, mysql.BinaryFlag, !aggregatedEvalType.IsStringKind() || gotBinString)
	return aggregatedEvalType
}

func mergeEvalType(lhs, rhs EvalType, lft, rft *FieldType, isLHSUnsigned, isRHSUnsigned bool) EvalType {
	if lft.GetType() == mysql.TypeUnspecified || rft.GetType() == mysql.TypeUnspecified {
		if lft.GetType() == rft.GetType() {
			return ETString
		}
		if lft.GetType() == mysql.TypeUnspecified {
			lhs = rhs
		} else {
			rhs = lhs
		}
	}
	if lhs.IsStringKind() || rhs.IsStringKind() {
		return ETString
	} else if lhs == ETReal || rhs == ETReal {
		return ETReal
	} else if lhs == ETDecimal || rhs == ETDecimal || isLHSUnsigned != isRHSUnsigned {
		return ETDecimal
	}
	return ETInt
}

// SetTypeFlag turns the flagItem on or off.
func SetTypeFlag(flag *uint, flagItem uint, on bool) {
	if on {
		*flag |= flagItem
	} else {
		*flag &= ^flagItem
	}
}

// InferParamTypeFromDatum is used for plan cache to infer the type of a parameter from its datum.
func InferParamTypeFromDatum(d *Datum, tp *FieldType) {
	InferParamTypeFromUnderlyingValue(d.GetValue(), tp)
	if IsStringKind(d.k) {
		// consider charset and collation here
		c, err := GetCollationByName(d.collation)
		if err != nil || c == nil {
			return // use default charset and collation
		}
		tp.SetCharset(c.CharsetName)
		tp.SetCollate(d.collation)
	}
}

// InferParamTypeFromUnderlyingValue is used for plan cache to infer the type of a parameter from its underlying value.
func InferParamTypeFromUnderlyingValue(value any, tp *FieldType) {
	switch value.(type) {
	case nil:
		tp.SetType(mysql.TypeVarString)
		tp.SetFlen(UnspecifiedLength)
		tp.SetDecimal(UnspecifiedLength)
		// Also set the `charset` and `collation` for it, because some function (e.g. `json_object`) will return error
		// if the argument collation is `binary`.
		tp.SetCharset(mysql.DefaultCharset)
		tp.SetCollate(mysql.DefaultCollationName)
	default:
		DefaultTypeForValue(value, tp, mysql.DefaultCharset, mysql.DefaultCollationName)
		if hasVariantFieldLength(tp) {
			tp.SetFlen(UnspecifiedLength)
		}
		if tp.GetType() == mysql.TypeUnspecified {
			tp.SetType(mysql.TypeVarString)
		}
	}
}

func hasVariantFieldLength(tp *FieldType) bool {
	switch tp.GetType() {
	case mysql.TypeLonglong, mysql.TypeVarString, mysql.TypeDouble, mysql.TypeBlob,
		mysql.TypeBit, mysql.TypeDuration, mysql.TypeEnum, mysql.TypeSet:
		return true
	}
	return false
}

// DefaultTypeForValue returns the default FieldType for the value.
func DefaultTypeForValue(value any, tp *FieldType, char string, collate string) {
	if value != nil {
		tp.AddFlag(mysql.NotNullFlag)
	}
	switch x := value.(type) {
	case nil:
		tp.SetType(mysql.TypeNull)
		tp.SetFlen(0)
		tp.SetDecimal(0)
		SetBinChsClnFlag(tp)
	case bool:
		tp.SetType(mysql.TypeLonglong)
		tp.SetFlen(1)
		tp.SetDecimal(0)
		tp.AddFlag(mysql.IsBooleanFlag)
		SetBinChsClnFlag(tp)
	case int:
		tp.SetType(mysql.TypeLonglong)
		tp.SetFlen(len(strconv.FormatInt(int64(x), 10)))
		tp.SetDecimal(0)
		SetBinChsClnFlag(tp)
	case int64:
		tp.SetType(mysql.TypeLonglong)
		tp.SetFlen(len(strconv.FormatInt(x, 10)))
		tp.SetDecimal(0)
		SetBinChsClnFlag(tp)
	case uint64:
		tp.SetType(mysql.TypeLonglong)
		tp.AddFlag(mysql.UnsignedFlag)
		tp.SetFlen(len(strconv.FormatUint(x, 10)))
		tp.SetDecimal(0)
		SetBinChsClnFlag(tp)
	case string:
		tp.SetType(mysql.TypeVarString)
		// TODO: tp.flen should be len(x) * 3 (max bytes length of CharsetUTF8)
		tp.SetFlen(len(x))
		tp.SetDecimal(UnspecifiedLength)
		tp.SetCharset(char)
		tp.SetCollate(collate)
	case float32:
		tp.SetType(mysql.TypeFloat)
		s := strconv.FormatFloat(float64(x), 'f', -1, 32)
		tp.SetFlen(len(s))
		tp.SetDecimal(UnspecifiedLength)
		SetBinChsClnFlag(tp)
	case float64:
		tp.SetType(mysql.TypeDouble)
		s := strconv.FormatFloat(x, 'f', -1, 64)
		tp.SetFlen(len(s))
		tp.SetDecimal(UnspecifiedLength)
		SetBinChsClnFlag(tp)
	case []byte:
		tp.SetType(mysql.TypeBlob)
		tp.SetFlen(len(x))
		tp.SetDecimal(UnspecifiedLength)
		SetBinChsClnFlag(tp)
	case BitLiteral:
		tp.SetType(mysql.TypeVarString)
		tp.SetFlen(len(x) * 3)
		tp.SetDecimal(0)
		SetBinChsClnFlag(tp)
	case HexLiteral:
		tp.SetType(mysql.TypeVarString)
		tp.SetFlen(len(x) * 3)
		tp.SetDecimal(0)
		tp.AddFlag(mysql.UnsignedFlag)
		SetBinChsClnFlag(tp)
	case BinaryLiteral:
		tp.SetType(mysql.TypeVarString)
		tp.SetFlen(len(x))
		tp.SetDecimal(0)
		SetBinChsClnFlag(tp)
		tp.DelFlag(mysql.BinaryFlag)
		tp.AddFlag(mysql.UnsignedFlag)
	case Time:
		tp.SetType(x.Type())
		switch x.Type() {
		case mysql.TypeDate:
			tp.SetFlen(mysql.MaxDateWidth)
			tp.SetDecimal(UnspecifiedLength)
		case mysql.TypeDatetime, mysql.TypeTimestamp:
			tp.SetFlen(mysql.MaxDatetimeWidthNoFsp)
			if x.Fsp() > DefaultFsp { // consider point('.') and the fractional part.
				tp.SetFlen(tp.GetFlen() + x.Fsp() + 1)
			}
			tp.SetDecimal(x.Fsp())
		}
		SetBinChsClnFlag(tp)
	case Duration:
		tp.SetType(mysql.TypeDuration)
		tp.SetFlen(len(x.String()))
		if x.Fsp > DefaultFsp { // consider point('.') and the fractional part.
			tp.SetFlen(x.Fsp + 1)
		}
		tp.SetDecimal(x.Fsp)
		SetBinChsClnFlag(tp)
	case *MyDecimal:
		tp.SetType(mysql.TypeNewDecimal)
		tp.SetFlenUnderLimit(len(x.ToString()))
		tp.SetDecimalUnderLimit(int(x.digitsFrac))
		// Add the length for `.`.
		tp.SetFlenUnderLimit(tp.GetFlen() + 1)
		SetBinChsClnFlag(tp)
	case Enum:
		tp.SetType(mysql.TypeEnum)
		tp.SetFlen(len(x.Name))
		tp.SetDecimal(UnspecifiedLength)
		SetBinChsClnFlag(tp)
	case Set:
		tp.SetType(mysql.TypeSet)
		tp.SetFlen(len(x.Name))
		tp.SetDecimal(UnspecifiedLength)
		SetBinChsClnFlag(tp)
	case BinaryJSON:
		tp.SetType(mysql.TypeJSON)
		tp.SetFlen(UnspecifiedLength)
		tp.SetDecimal(0)
		tp.SetCharset(CharsetUTF8MB4)
		tp.SetCollate(CollationUTF8MB4)
	case VectorFloat32:
		tp.SetType(mysql.TypeTiDBVectorFloat32)
		tp.SetFlen(UnspecifiedLength)
		tp.SetDecimal(0)
		SetBinChsClnFlag(tp)
	default:
		tp.SetType(mysql.TypeUnspecified)
		tp.SetFlen(UnspecifiedLength)
		tp.SetDecimal(UnspecifiedLength)
		tp.SetCharset(CharsetUTF8MB4)
		tp.SetCollate(CollationUTF8MB4)
	}
}

// minFlenAndDecimalForType returns the minimum flen/decimal that can hold all the data for `tp`.
func minFlenAndDecimalForType(tp byte) (int, int) {
	switch tp {
	case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear:
		return mysql.GetDefaultFieldLengthAndDecimal(tp)
	default:
		// todo support non-integer type
		return UnspecifiedLength, UnspecifiedLength
	}
}

// DefaultCharsetForType returns the default charset/collation for mysql type.
func DefaultCharsetForType(tp byte) (defaultCharset string, defaultCollation string) {
	switch tp {
	case mysql.TypeVarString, mysql.TypeString, mysql.TypeVarchar:
		// Default charset for string types is utf8mb4.
		return mysql.DefaultCharset, mysql.DefaultCollationName
	}
	return CharsetBin, CollationBin
}

// mergeFieldType merges two MySQL type to a new type.
// This is used in hybrid field type expression.
// For example "select case c when 1 then 2 when 2 then 'tidb' from t;"
// The result field type of the case expression is the merged type of the two when clause.
// See https://github.com/mysql/mysql-server/blob/8.0/sql/field.cc#L1042
//
// This function doesn't handle the range bump: for example, when the unsigned long is merged with signed long,
// the result should be longlong. However, this function returns long for this case. Please use `AggFieldType`
// function if you need to handle the range bump.
func mergeFieldType(a byte, b byte) byte {
	ia := getFieldTypeIndex(a)
	ib := getFieldTypeIndex(b)
	return fieldTypeMergeRules[ia][ib]
}

// mergeTypeFlag merges two MySQL type flag to a new one
// currently only NotNullFlag and UnsignedFlag is checked
// todo more flag need to be checked
func mergeTypeFlag(a, b uint) uint {
	return a & (b&mysql.NotNullFlag | ^mysql.NotNullFlag) & (b&mysql.UnsignedFlag | ^mysql.UnsignedFlag)
}

var (
	fieldTypeIndexes = map[byte]int{
		mysql.TypeUnspecified:       0,
		mysql.TypeTiny:              1,
		mysql.TypeShort:             2,
		mysql.TypeLong:              3,
		mysql.TypeFloat:             4,
		mysql.TypeDouble:            5,
		mysql.TypeNull:              6,
		mysql.TypeTimestamp:         7,
		mysql.TypeLonglong:          8,
		mysql.TypeInt24:             9,
		mysql.TypeDate:              10,
		mysql.TypeDuration:          11,
		mysql.TypeDatetime:          12,
		mysql.TypeYear:              13,
		mysql.TypeNewDate:           14,
		mysql.TypeVarchar:           15,
		mysql.TypeBit:               16,
		mysql.TypeJSON:              17,
		mysql.TypeNewDecimal:        18,
		mysql.TypeEnum:              19,
		mysql.TypeSet:               20,
		mysql.TypeTinyBlob:          21,
		mysql.TypeMediumBlob:        22,
		mysql.TypeLongBlob:          23,
		mysql.TypeBlob:              24,
		mysql.TypeVarString:         25,
		mysql.TypeString:            26,
		mysql.TypeGeometry:          27,
		mysql.TypeTiDBVectorFloat32: 28,
	}
)

func getFieldTypeIndex(tp byte) int {
	return fieldTypeIndexes[tp]
}

// https://github.com/mysql/mysql-server/blob/8.0/sql/field.cc#L248
var fieldTypeMergeRules = [29][29]byte{
	/* mysql.TypeUnspecified -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeNewDecimal, mysql.TypeNewDecimal,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeNewDecimal, mysql.TypeNewDecimal,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeDouble, mysql.TypeDouble,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeNewDecimal, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeUnspecified, mysql.TypeUnspecified,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeVarchar,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeNewDecimal, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeTiny -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeNewDecimal, mysql.TypeTiny,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeShort, mysql.TypeLong,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeFloat, mysql.TypeDouble,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeTiny, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeLonglong, mysql.TypeInt24,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeTiny,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeLonglong,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeNewDecimal, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeShort -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeNewDecimal, mysql.TypeShort,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeShort, mysql.TypeLong,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeFloat, mysql.TypeDouble,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeShort, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeLonglong, mysql.TypeInt24,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeShort,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeLonglong,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeNewDecimal, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeLong -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeNewDecimal, mysql.TypeLong,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeLong, mysql.TypeLong,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeDouble, mysql.TypeDouble,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeLong, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeLonglong, mysql.TypeLong,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeLong,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeLonglong,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeNewDecimal, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeFloat -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeDouble, mysql.TypeFloat,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeFloat, mysql.TypeDouble,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeFloat, mysql.TypeDouble,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeFloat, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeFloat, mysql.TypeFloat,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeFloat,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeDouble,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeDouble, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeDouble -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeDouble, mysql.TypeDouble,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeDouble, mysql.TypeDouble,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeDouble, mysql.TypeDouble,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeDouble, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeDouble, mysql.TypeDouble,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeDouble,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeDouble,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeDouble, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeNull -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeNewDecimal, mysql.TypeTiny,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeShort, mysql.TypeLong,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeFloat, mysql.TypeDouble,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeNull, mysql.TypeTimestamp,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeLonglong, mysql.TypeLonglong,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeDate, mysql.TypeDuration,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeDatetime, mysql.TypeYear,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeNewDate, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeBit,
		// mysql.TypeJSON
		mysql.TypeJSON,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeNewDecimal, mysql.TypeEnum,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeSet, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeGeometry,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeTiDBVectorFloat32,
	},
	/* mysql.TypeTimestamp -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeTimestamp, mysql.TypeTimestamp,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeDatetime, mysql.TypeDatetime,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeDatetime, mysql.TypeVarchar,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeNewDate, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeVarchar,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeLonglong -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeNewDecimal, mysql.TypeLonglong,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeLonglong, mysql.TypeLonglong,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeDouble, mysql.TypeDouble,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeLonglong, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeLonglong, mysql.TypeLong,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeLonglong,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeNewDate, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeLonglong,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeNewDecimal, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeInt24 -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeNewDecimal, mysql.TypeInt24,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeInt24, mysql.TypeLong,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeFloat, mysql.TypeDouble,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeInt24, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeLonglong, mysql.TypeInt24,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeInt24,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeNewDate, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeLonglong,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal    mysql.TypeEnum
		mysql.TypeNewDecimal, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeDate -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeDate, mysql.TypeDatetime,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeDate, mysql.TypeDatetime,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeDatetime, mysql.TypeVarchar,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeNewDate, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeVarchar,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeTime -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeDuration, mysql.TypeDatetime,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeDatetime, mysql.TypeDuration,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeDatetime, mysql.TypeVarchar,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeNewDate, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeVarchar,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeDatetime -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeDatetime, mysql.TypeDatetime,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeDatetime, mysql.TypeDatetime,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeDatetime, mysql.TypeVarchar,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeNewDate, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeVarchar,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeYear -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeUnspecified, mysql.TypeTiny,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeShort, mysql.TypeLong,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeFloat, mysql.TypeDouble,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeYear, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeLonglong, mysql.TypeInt24,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeYear,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeLonglong,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeNewDecimal, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeNewDate -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeNewDate, mysql.TypeDatetime,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeNewDate, mysql.TypeDatetime,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeDatetime, mysql.TypeVarchar,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeNewDate, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeVarchar,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeVarchar -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeVarchar,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeBit -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeVarchar, mysql.TypeLonglong,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeLonglong, mysql.TypeLonglong,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeDouble, mysql.TypeDouble,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeBit, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeLonglong, mysql.TypeLonglong,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeLonglong,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeBit,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeNewDecimal, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeJSON -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNewFloat     mysql.TypeDouble
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeJSON, mysql.TypeVarchar,
		// mysql.TypeLongLONG     mysql.TypeInt24
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDate         MYSQL_TYPE_TIME
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     MYSQL_TYPE_YEAR
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeVarchar,
		// mysql.TypeJSON
		mysql.TypeJSON,
		// mysql.TypeNewDecimal   MYSQL_TYPE_ENUM
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeLongBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeLongBlob, mysql.TypeVarchar,
		// mysql.TypeString       MYSQL_TYPE_GEOMETRY
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeNewDecimal -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeNewDecimal, mysql.TypeNewDecimal,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeNewDecimal, mysql.TypeNewDecimal,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeDouble, mysql.TypeDouble,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeNewDecimal, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeNewDecimal, mysql.TypeNewDecimal,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeNewDecimal,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeNewDecimal,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeNewDecimal, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeEnum -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeEnum, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeVarchar,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeSet -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeSet, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeVarchar,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeTinyBlob -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeTinyBlob, mysql.TypeTinyBlob,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeTinyBlob, mysql.TypeTinyBlob,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeTinyBlob, mysql.TypeTinyBlob,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeTinyBlob, mysql.TypeTinyBlob,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeTinyBlob, mysql.TypeTinyBlob,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeTinyBlob, mysql.TypeTinyBlob,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeTinyBlob, mysql.TypeTinyBlob,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeTinyBlob, mysql.TypeTinyBlob,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeTinyBlob,
		// mysql.TypeJSON
		mysql.TypeLongBlob,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeTinyBlob, mysql.TypeTinyBlob,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeTinyBlob, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeTinyBlob,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeTinyBlob, mysql.TypeTinyBlob,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeLongBlob,
	},
	/* mysql.TypeMediumBlob -> */
	{
		// mysql.TypeUnspecified    mysql.TypeTiny
		mysql.TypeMediumBlob, mysql.TypeMediumBlob,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeMediumBlob, mysql.TypeMediumBlob,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeMediumBlob, mysql.TypeMediumBlob,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeMediumBlob, mysql.TypeMediumBlob,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeMediumBlob, mysql.TypeMediumBlob,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeMediumBlob, mysql.TypeMediumBlob,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeMediumBlob, mysql.TypeMediumBlob,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeMediumBlob, mysql.TypeMediumBlob,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeMediumBlob,
		// mysql.TypeJSON
		mysql.TypeLongBlob,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeMediumBlob, mysql.TypeMediumBlob,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeMediumBlob, mysql.TypeMediumBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeMediumBlob, mysql.TypeMediumBlob,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeMediumBlob, mysql.TypeMediumBlob,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeLongBlob,
	},
	/* mysql.TypeLongBlob -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeLongBlob,
		// mysql.TypeJSON
		mysql.TypeLongBlob,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeLongBlob,
	},
	/* mysql.TypeBlob -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeBlob, mysql.TypeBlob,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeBlob, mysql.TypeBlob,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeBlob, mysql.TypeBlob,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeBlob, mysql.TypeBlob,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeBlob, mysql.TypeBlob,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeBlob, mysql.TypeBlob,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeBlob, mysql.TypeBlob,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeBlob, mysql.TypeBlob,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeBlob,
		// mysql.TypeJSON
		mysql.TypeLongBlob,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeBlob, mysql.TypeBlob,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeBlob, mysql.TypeBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeBlob,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeBlob, mysql.TypeBlob,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeLongBlob,
	},
	/* mysql.TypeVarString -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeVarchar,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeString -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeString, mysql.TypeString,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeString, mysql.TypeString,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeString, mysql.TypeString,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeString, mysql.TypeString,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeString, mysql.TypeString,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeString, mysql.TypeString,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeString, mysql.TypeString,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeString,
		// mysql.TypeJSON
		mysql.TypeString,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeString, mysql.TypeString,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeString, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeString,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeString,
	},
	/* mysql.TypeGeometry -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeFloat        mysql.TypeDouble
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeGeometry, mysql.TypeVarchar,
		// mysql.TypeLonglong     mysql.TypeInt24
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDate         mysql.TypeTime
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     mysql.TypeYear
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeVarchar,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   mysql.TypeEnum
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeTinyBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeMediumBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeBlob, mysql.TypeVarchar,
		// mysql.TypeString       mysql.TypeGeometry
		mysql.TypeString, mysql.TypeGeometry,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeVarchar,
	},
	/* mysql.TypeTiDBVectorFloat32 -> */
	{
		// mysql.TypeUnspecified  mysql.TypeTiny
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeShort        mysql.TypeLong
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNewFloat     mysql.TypeDouble
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNull         mysql.TypeTimestamp
		mysql.TypeTiDBVectorFloat32, mysql.TypeVarchar,
		// mysql.TypeLongLONG     mysql.TypeInt24
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDate         MYSQL_TYPE_TIME
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeDatetime     MYSQL_TYPE_YEAR
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeNewDate      mysql.TypeVarchar
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeBit          <16>-<244>
		mysql.TypeVarchar,
		// mysql.TypeJSON
		mysql.TypeVarchar,
		// mysql.TypeNewDecimal   MYSQL_TYPE_ENUM
		mysql.TypeVarchar, mysql.TypeVarchar,
		// mysql.TypeSet          mysql.TypeTinyBlob
		mysql.TypeVarchar, mysql.TypeLongBlob,
		// mysql.TypeMediumBlob  mysql.TypeLongBlob
		mysql.TypeLongBlob, mysql.TypeLongBlob,
		// mysql.TypeBlob         mysql.TypeVarString
		mysql.TypeLongBlob, mysql.TypeVarchar,
		// mysql.TypeString       MYSQL_TYPE_GEOMETRY
		mysql.TypeString, mysql.TypeVarchar,
		// mysql.TypeTiDBVectorFloat32
		mysql.TypeTiDBVectorFloat32,
	},
}

// SetBinChsClnFlag sets charset, collation as 'binary' and adds binaryFlag to FieldType.
func SetBinChsClnFlag(ft *FieldType) {
	ft.SetCharset(CharsetBin)
	ft.SetCollate(CollationBin)
	ft.AddFlag(mysql.BinaryFlag)
}

// VarStorageLen indicates this column is a variable length column.
const VarStorageLen = -1

// CheckModifyTypeCompatible checks whether changes column type to another is compatible and can be changed.
// If types are compatible and can be directly changed, nil err will be returned; otherwise the types are incompatible.
// There are two cases when types incompatible:
// 1. returned canReorg == true: types can be changed by reorg
// 2. returned canReorg == false: type change not supported yet
func CheckModifyTypeCompatible(origin *FieldType, to *FieldType) (canReorg bool, err error) {
	// Deal with the same type.
	if origin.GetType() == to.GetType() {
		if origin.GetType() == mysql.TypeEnum || origin.GetType() == mysql.TypeSet {
			typeVar := "set"
			if origin.GetType() == mysql.TypeEnum {
				typeVar = "enum"
			}
			if len(to.GetElems()) < len(origin.GetElems()) {
				msg := fmt.Sprintf("the number of %s column's elements is less than the original: %d", typeVar, len(origin.GetElems()))
				return true, ErrUnsupportedModifyColumn.GenWithStackByArgs(msg)
			}
			for index, originElem := range origin.GetElems() {
				toElem := to.GetElems()[index]
				if originElem != toElem {
					msg := fmt.Sprintf("cannot modify %s column value %s to %s", typeVar, originElem, toElem)
					return true, ErrUnsupportedModifyColumn.GenWithStackByArgs(msg)
				}
			}
		}

		if origin.GetType() == mysql.TypeNewDecimal {
			// Floating-point and fixed-point types also can be UNSIGNED. As with integer types, this attribute prevents
			// negative values from being stored in the column. Unlike the integer types, the upper range of column values
			// remains the same.
			if to.GetFlen() != origin.GetFlen() || to.GetDecimal() != origin.GetDecimal() || mysql.HasUnsignedFlag(to.GetFlag()) != mysql.HasUnsignedFlag(origin.GetFlag()) {
				msg := fmt.Sprintf("decimal change from decimal(%d, %d) to decimal(%d, %d)", origin.GetFlen(), origin.GetDecimal(), to.GetFlen(), to.GetDecimal())
				return true, ErrUnsupportedModifyColumn.GenWithStackByArgs(msg)
			}
		}

		needReorg, reason := needReorgToChange(origin, to)
		if !needReorg {
			return false, nil
		}
		return true, ErrUnsupportedModifyColumn.GenWithStackByArgs(reason)
	}

	// Deal with the different type.
	if !checkTypeChangeSupported(origin, to) {
		unsupportedMsg := fmt.Sprintf("change from original type %v to %v is currently unsupported yet", origin.CompactStr(), to.CompactStr())
		return false, ErrUnsupportedModifyColumn.GenWithStackByArgs(unsupportedMsg)
	}

	// Check if different type can directly convert and no need to reorg.
	stringToString := IsString(origin.GetType()) && IsString(to.GetType())
	integerToInteger := mysql.IsIntegerType(origin.GetType()) && mysql.IsIntegerType(to.GetType())
	if stringToString || integerToInteger {
		needReorg, reason := needReorgToChange(origin, to)
		if !needReorg {
			return false, nil
		}
		return true, ErrUnsupportedModifyColumn.GenWithStackByArgs(reason)
	}

	notCompatibleMsg := fmt.Sprintf("type %v not match origin %v", to.CompactStr(), origin.CompactStr())
	return true, ErrUnsupportedModifyColumn.GenWithStackByArgs(notCompatibleMsg)
}

func needReorgToChange(origin *FieldType, to *FieldType) (needReorg bool, reasonMsg string) {
	toFlen := to.GetFlen()
	originFlen := origin.GetFlen()
	if mysql.IsIntegerType(to.GetType()) && mysql.IsIntegerType(origin.GetType()) {
		// For integers, we should ignore the potential display length represented by flen, using
		// the default flen of the type.
		originFlen, _ = mysql.GetDefaultFieldLengthAndDecimal(origin.GetType())
		toFlen, _ = mysql.GetDefaultFieldLengthAndDecimal(to.GetType())
	}

	if ConvertBetweenCharAndVarchar(origin.GetType(), to.GetType()) {
		return true, "conversion between char and varchar string needs reorganization"
	}

	if toFlen > 0 && toFlen != originFlen {
		if toFlen < originFlen {
			return true, fmt.Sprintf("length %d is less than origin %d", toFlen, originFlen)
		}

		// Due to the behavior of padding \x00 at binary type, we need to reorg when binary length changed
		isBinaryType := func(tp *FieldType) bool { return tp.GetType() == mysql.TypeString && IsBinaryStr(tp) }
		if isBinaryType(origin) && isBinaryType(to) {
			return true, "can't change binary types of different length"
		}
	}
	if to.GetDecimal() > 0 && to.GetDecimal() < origin.GetDecimal() {
		return true, fmt.Sprintf("decimal %d is less than origin %d", to.GetDecimal(), origin.GetDecimal())
	}
	if mysql.HasUnsignedFlag(origin.GetFlag()) != mysql.HasUnsignedFlag(to.GetFlag()) {
		return true, "can't change unsigned integer to signed or vice versa"
	}
	return false, ""
}

func checkTypeChangeSupported(origin *FieldType, to *FieldType) bool {
	if (IsTypeTime(origin.GetType()) || origin.GetType() == mysql.TypeDuration || origin.GetType() == mysql.TypeYear ||
		IsString(origin.GetType()) || origin.GetType() == mysql.TypeJSON) &&
		to.GetType() == mysql.TypeBit {
		// TODO: Currently date/datetime/timestamp/time/year/string/json data type cast to bit are not compatible with mysql, should fix here after compatible.
		return false
	}

	if (IsTypeTime(origin.GetType()) || origin.GetType() == mysql.TypeDuration || origin.GetType() == mysql.TypeYear ||
		origin.GetType() == mysql.TypeNewDecimal || origin.GetType() == mysql.TypeFloat || origin.GetType() == mysql.TypeDouble || origin.GetType() == mysql.TypeJSON || origin.GetType() == mysql.TypeBit) &&
		(to.GetType() == mysql.TypeEnum || to.GetType() == mysql.TypeSet) {
		// TODO: Currently date/datetime/timestamp/time/year/decimal/float/double/json/bit cast to enum/set are not support yet, should fix here after supported.
		return false
	}

	if (origin.GetType() == mysql.TypeEnum || origin.GetType() == mysql.TypeSet || origin.GetType() == mysql.TypeBit ||
		origin.GetType() == mysql.TypeNewDecimal || origin.GetType() == mysql.TypeFloat || origin.GetType() == mysql.TypeDouble) &&
		(IsTypeTime(to.GetType())) {
		// TODO: Currently enum/set/bit/decimal/float/double cast to date/datetime/timestamp type are not support yet, should fix here after supported.
		return false
	}

	if origin.GetType() == mysql.TypeTiDBVectorFloat32 || to.GetType() == mysql.TypeTiDBVectorFloat32 {
		// TODO: Vector type not supported.
		return false
	}

	if (origin.GetType() == mysql.TypeEnum || origin.GetType() == mysql.TypeSet || origin.GetType() == mysql.TypeBit) &&
		to.GetType() == mysql.TypeDuration {
		// TODO: Currently enum/set/bit cast to time are not support yet, should fix here after supported.
		return false
	}

	return true
}

// ConvertBetweenCharAndVarchar is that Column type conversion between varchar to char need reorganization because
// 1. varchar -> char: char type is stored with the padding removed. All the indexes need to be rewritten.
// 2. char -> varchar: the index value encoding of secondary index on clustered primary key tables is different.
// These secondary indexes need to be rewritten.
func ConvertBetweenCharAndVarchar(oldCol, newCol byte) bool {
	return (IsTypeVarchar(oldCol) && newCol == mysql.TypeString) ||
		(oldCol == mysql.TypeString && IsTypeVarchar(newCol) && NewCollationEnabled())
}

// IsVarcharTooBigFieldLength check if the varchar type column exceeds the maximum length limit.
func IsVarcharTooBigFieldLength(colDefTpFlen int, colDefName, setCharset string) error {
	desc, err := GetCharsetInfo(setCharset)
	if err != nil {
		return errors.Trace(err)
	}
	maxFlen := mysql.MaxFieldVarCharLength
	maxFlen /= desc.Maxlen
	if colDefTpFlen != UnspecifiedLength && colDefTpFlen > maxFlen {
		return ErrTooBigFieldLength.GenWithStack("Column length too big for column '%s' (max = %d); use BLOB or TEXT instead", colDefName, maxFlen)
	}
	return nil
}
