github.com/team-ide/go-dialect@v1.9.20/vitess/sqltypes/type.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package sqltypes
    18  
    19  import (
    20  	"fmt"
    21  
    22  	querypb "github.com/team-ide/go-dialect/vitess/query"
    23  )
    24  
    25  // This file provides wrappers and support
    26  // functions for querypb.Type.
    27  
    28  // These bit flags can be used to query on the
    29  // common properties of types.
    30  const (
    31  	flagIsIntegral = int(querypb.Flag_ISINTEGRAL)
    32  	flagIsUnsigned = int(querypb.Flag_ISUNSIGNED)
    33  	flagIsFloat    = int(querypb.Flag_ISFLOAT)
    34  	flagIsQuoted   = int(querypb.Flag_ISQUOTED)
    35  	flagIsText     = int(querypb.Flag_ISTEXT)
    36  	flagIsBinary   = int(querypb.Flag_ISBINARY)
    37  )
    38  
    39  // IsIntegral returns true if querypb.Type is an integral
    40  // (signed/unsigned) that can be represented using
    41  // up to 64 binary bits.
    42  // If you have a Value object, use its member function.
    43  func IsIntegral(t querypb.Type) bool {
    44  	return int(t)&flagIsIntegral == flagIsIntegral
    45  }
    46  
    47  // IsSigned returns true if querypb.Type is a signed integral.
    48  // If you have a Value object, use its member function.
    49  func IsSigned(t querypb.Type) bool {
    50  	return int(t)&(flagIsIntegral|flagIsUnsigned) == flagIsIntegral
    51  }
    52  
    53  // IsUnsigned returns true if querypb.Type is an unsigned integral.
    54  // Caution: this is not the same as !IsSigned.
    55  // If you have a Value object, use its member function.
    56  func IsUnsigned(t querypb.Type) bool {
    57  	return int(t)&(flagIsIntegral|flagIsUnsigned) == flagIsIntegral|flagIsUnsigned
    58  }
    59  
    60  // IsFloat returns true is querypb.Type is a floating point.
    61  // If you have a Value object, use its member function.
    62  func IsFloat(t querypb.Type) bool {
    63  	return int(t)&flagIsFloat == flagIsFloat
    64  }
    65  
    66  // IsQuoted returns true if querypb.Type is a quoted text or binary.
    67  // If you have a Value object, use its member function.
    68  func IsQuoted(t querypb.Type) bool {
    69  	return (int(t)&flagIsQuoted == flagIsQuoted) && t != Bit
    70  }
    71  
    72  // IsText returns true if querypb.Type is a text.
    73  // If you have a Value object, use its member function.
    74  func IsText(t querypb.Type) bool {
    75  	return int(t)&flagIsText == flagIsText
    76  }
    77  
    78  // IsBinary returns true if querypb.Type is a binary.
    79  // If you have a Value object, use its member function.
    80  func IsBinary(t querypb.Type) bool {
    81  	return int(t)&flagIsBinary == flagIsBinary
    82  }
    83  
    84  // IsNumber returns true if the type is any type of number.
    85  func IsNumber(t querypb.Type) bool {
    86  	return IsIntegral(t) || IsFloat(t) || t == Decimal
    87  }
    88  
    89  // IsDate returns true if the type represents a date and/or time.
    90  func IsDate(t querypb.Type) bool {
    91  	return t == Datetime || t == Date || t == Timestamp || t == Time
    92  }
    93  
    94  // IsNull returns true if the type is NULL type
    95  func IsNull(t querypb.Type) bool {
    96  	return t == Null
    97  }
    98  
    99  // Vitess data types. These are idiomatically
   100  // named synonyms for the querypb.Type values.
   101  // Although these constants are interchangeable,
   102  // they should be treated as different from querypb.Type.
   103  // Use the synonyms only to refer to the type in Value.
   104  // For proto variables, use the querypb.Type constants
   105  // instead.
   106  // The following conditions are non-overlapping
   107  // and cover all types: IsSigned(), IsUnsigned(),
   108  // IsFloat(), IsQuoted(), Null, Decimal, Expression, Bit
   109  // Also, IsIntegral() == (IsSigned()||IsUnsigned()).
   110  // TestCategory needs to be updated accordingly if
   111  // you add a new type.
   112  // If IsBinary or IsText is true, then IsQuoted is
   113  // also true. But there are IsQuoted types that are
   114  // neither binary or text.
   115  // querypb.Type_TUPLE is not included in this list
   116  // because it's not a valid Value type.
   117  // TODO(sougou): provide a categorization function
   118  // that returns enums, which will allow for cleaner
   119  // switch statements for those who want to cover types
   120  // by their category.
   121  const (
   122  	Null       = querypb.Type_NULL_TYPE
   123  	Int8       = querypb.Type_INT8
   124  	Uint8      = querypb.Type_UINT8
   125  	Int16      = querypb.Type_INT16
   126  	Uint16     = querypb.Type_UINT16
   127  	Int24      = querypb.Type_INT24
   128  	Uint24     = querypb.Type_UINT24
   129  	Int32      = querypb.Type_INT32
   130  	Uint32     = querypb.Type_UINT32
   131  	Int64      = querypb.Type_INT64
   132  	Uint64     = querypb.Type_UINT64
   133  	Float32    = querypb.Type_FLOAT32
   134  	Float64    = querypb.Type_FLOAT64
   135  	Timestamp  = querypb.Type_TIMESTAMP
   136  	Date       = querypb.Type_DATE
   137  	Time       = querypb.Type_TIME
   138  	Datetime   = querypb.Type_DATETIME
   139  	Year       = querypb.Type_YEAR
   140  	Decimal    = querypb.Type_DECIMAL
   141  	Text       = querypb.Type_TEXT
   142  	Blob       = querypb.Type_BLOB
   143  	VarChar    = querypb.Type_VARCHAR
   144  	VarBinary  = querypb.Type_VARBINARY
   145  	Char       = querypb.Type_CHAR
   146  	Binary     = querypb.Type_BINARY
   147  	Bit        = querypb.Type_BIT
   148  	Enum       = querypb.Type_ENUM
   149  	Set        = querypb.Type_SET
   150  	Geometry   = querypb.Type_GEOMETRY
   151  	TypeJSON   = querypb.Type_JSON
   152  	Expression = querypb.Type_EXPRESSION
   153  	HexNum     = querypb.Type_HEXNUM
   154  	HexVal     = querypb.Type_HEXVAL
   155  )
   156  
   157  // bit-shift the mysql flags by two byte so we
   158  // can merge them with the mysql or vitess types.
   159  const (
   160  	mysqlUnsigned = 32
   161  	mysqlBinary   = 128
   162  	mysqlEnum     = 256
   163  	mysqlSet      = 2048
   164  )
   165  
   166  // If you add to this map, make sure you add a test case
   167  // in tabletserver/endtoend.
   168  var mysqlToType = map[int64]querypb.Type{
   169  	0:   Decimal,
   170  	1:   Int8,
   171  	2:   Int16,
   172  	3:   Int32,
   173  	4:   Float32,
   174  	5:   Float64,
   175  	6:   Null,
   176  	7:   Timestamp,
   177  	8:   Int64,
   178  	9:   Int24,
   179  	10:  Date,
   180  	11:  Time,
   181  	12:  Datetime,
   182  	13:  Year,
   183  	15:  VarChar,
   184  	16:  Bit,
   185  	17:  Timestamp,
   186  	18:  Datetime,
   187  	19:  Time,
   188  	245: TypeJSON,
   189  	246: Decimal,
   190  	247: Enum,
   191  	248: Set,
   192  	249: Text,
   193  	250: Text,
   194  	251: Text,
   195  	252: Text,
   196  	253: VarChar,
   197  	254: Char,
   198  	255: Geometry,
   199  }
   200  
   201  // modifyType modifies the vitess type based on the
   202  // mysql flag. The function checks specific flags based
   203  // on the type. This allows us to ignore stray flags
   204  // that MySQL occasionally sets.
   205  func modifyType(typ querypb.Type, flags int64) querypb.Type {
   206  	switch typ {
   207  	case Int8:
   208  		if flags&mysqlUnsigned != 0 {
   209  			return Uint8
   210  		}
   211  	case Int16:
   212  		if flags&mysqlUnsigned != 0 {
   213  			return Uint16
   214  		}
   215  	case Int32:
   216  		if flags&mysqlUnsigned != 0 {
   217  			return Uint32
   218  		}
   219  	case Int64:
   220  		if flags&mysqlUnsigned != 0 {
   221  			return Uint64
   222  		}
   223  	case Int24:
   224  		if flags&mysqlUnsigned != 0 {
   225  			return Uint24
   226  		}
   227  	case Text:
   228  		if flags&mysqlBinary != 0 {
   229  			return Blob
   230  		}
   231  	case VarChar:
   232  		if flags&mysqlBinary != 0 {
   233  			return VarBinary
   234  		}
   235  	case Char:
   236  		if flags&mysqlBinary != 0 {
   237  			return Binary
   238  		}
   239  		if flags&mysqlEnum != 0 {
   240  			return Enum
   241  		}
   242  		if flags&mysqlSet != 0 {
   243  			return Set
   244  		}
   245  	case Year:
   246  		if flags&mysqlBinary != 0 {
   247  			return VarBinary
   248  		}
   249  	}
   250  	return typ
   251  }
   252  
   253  // MySQLToType computes the vitess type from mysql type and flags.
   254  func MySQLToType(mysqlType, flags int64) (typ querypb.Type, err error) {
   255  	result, ok := mysqlToType[mysqlType]
   256  	if !ok {
   257  		return 0, fmt.Errorf("unsupported type: %d", mysqlType)
   258  	}
   259  	return modifyType(result, flags), nil
   260  }
   261  
   262  // AreTypesEquivalent returns whether two types are equivalent.
   263  func AreTypesEquivalent(mysqlTypeFromBinlog, mysqlTypeFromSchema querypb.Type) bool {
   264  	return (mysqlTypeFromBinlog == mysqlTypeFromSchema) ||
   265  		(mysqlTypeFromBinlog == VarChar && mysqlTypeFromSchema == VarBinary) ||
   266  		// Binlog only has base type. But doesn't have per-column-flags to differentiate
   267  		// various logical types. For Binary, Enum, Set types, binlog only returns Char
   268  		// as data type.
   269  		(mysqlTypeFromBinlog == Char && mysqlTypeFromSchema == Binary) ||
   270  		(mysqlTypeFromBinlog == Char && mysqlTypeFromSchema == Enum) ||
   271  		(mysqlTypeFromBinlog == Char && mysqlTypeFromSchema == Set) ||
   272  		(mysqlTypeFromBinlog == Text && mysqlTypeFromSchema == Blob) ||
   273  		(mysqlTypeFromBinlog == Int8 && mysqlTypeFromSchema == Uint8) ||
   274  		(mysqlTypeFromBinlog == Int16 && mysqlTypeFromSchema == Uint16) ||
   275  		(mysqlTypeFromBinlog == Int24 && mysqlTypeFromSchema == Uint24) ||
   276  		(mysqlTypeFromBinlog == Int32 && mysqlTypeFromSchema == Uint32) ||
   277  		(mysqlTypeFromBinlog == Int64 && mysqlTypeFromSchema == Uint64)
   278  }
   279  
   280  // typeToMySQL is the reverse of mysqlToType.
   281  var typeToMySQL = map[querypb.Type]struct {
   282  	typ   int64
   283  	flags int64
   284  }{
   285  	Int8:      {typ: 1},
   286  	Uint8:     {typ: 1, flags: mysqlUnsigned},
   287  	Int16:     {typ: 2},
   288  	Uint16:    {typ: 2, flags: mysqlUnsigned},
   289  	Int32:     {typ: 3},
   290  	Uint32:    {typ: 3, flags: mysqlUnsigned},
   291  	Float32:   {typ: 4},
   292  	Float64:   {typ: 5},
   293  	Null:      {typ: 6, flags: mysqlBinary},
   294  	Timestamp: {typ: 7},
   295  	Int64:     {typ: 8},
   296  	Uint64:    {typ: 8, flags: mysqlUnsigned},
   297  	Int24:     {typ: 9},
   298  	Uint24:    {typ: 9, flags: mysqlUnsigned},
   299  	Date:      {typ: 10, flags: mysqlBinary},
   300  	Time:      {typ: 11, flags: mysqlBinary},
   301  	Datetime:  {typ: 12, flags: mysqlBinary},
   302  	Year:      {typ: 13, flags: mysqlUnsigned},
   303  	Bit:       {typ: 16, flags: mysqlUnsigned},
   304  	TypeJSON:  {typ: 245},
   305  	Decimal:   {typ: 246},
   306  	Text:      {typ: 252},
   307  	Blob:      {typ: 252, flags: mysqlBinary},
   308  	VarChar:   {typ: 253},
   309  	VarBinary: {typ: 253, flags: mysqlBinary},
   310  	Char:      {typ: 254},
   311  	Binary:    {typ: 254, flags: mysqlBinary},
   312  	Enum:      {typ: 254, flags: mysqlEnum},
   313  	Set:       {typ: 254, flags: mysqlSet},
   314  	Geometry:  {typ: 255},
   315  }
   316  
   317  // TypeToMySQL returns the equivalent mysql type and flag for a vitess type.
   318  func TypeToMySQL(typ querypb.Type) (mysqlType, flags int64) {
   319  	val := typeToMySQL[typ]
   320  	return val.typ, val.flags
   321  }