github.com/pingcap/tidb/parser@v0.0.0-20231013125129-93a834a6bf8d/types/field_type.go (about)

     1  // Copyright 2015 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package types
    15  
    16  import (
    17  	"encoding/json"
    18  	"fmt"
    19  	"io"
    20  	"strings"
    21  	"unsafe"
    22  
    23  	"github.com/cznic/mathutil"
    24  	"github.com/pingcap/tidb/parser/charset"
    25  	"github.com/pingcap/tidb/parser/format"
    26  	"github.com/pingcap/tidb/parser/mysql"
    27  )
    28  
    29  // UnspecifiedLength is unspecified length.
    30  const (
    31  	UnspecifiedLength = -1
    32  )
    33  
    34  // TiDBStrictIntegerDisplayWidth represent whether return warnings when integerType with (length) was parsed.
    35  // The default is `false`, it will be parsed as warning, and the result in show-create-table will ignore the
    36  // display length when it set to `true`. This is for compatibility with MySQL 8.0 in which integer max display
    37  // length is deprecated, referring this issue #6688 for more details.
    38  var (
    39  	TiDBStrictIntegerDisplayWidth bool
    40  )
    41  
    42  // FieldType records field type information.
    43  type FieldType struct {
    44  	// tp is type of the field
    45  	tp byte
    46  	// flag represent NotNull, Unsigned, PriKey flags etc.
    47  	flag uint
    48  	// flen represent size of bytes of the field
    49  	flen int
    50  	// decimal represent decimal length of the field
    51  	decimal int
    52  	// charset represent character set
    53  	charset string
    54  	// collate represent collate rules of the charset
    55  	collate string
    56  	// elems is the element list for enum and set type.
    57  	elems            []string
    58  	elemsIsBinaryLit []bool
    59  	array            bool
    60  	// Please keep in mind that jsonFieldType should be updated if you add a new field here.
    61  }
    62  
    63  // NewFieldType returns a FieldType,
    64  // with a type and other information about field type.
    65  func NewFieldType(tp byte) *FieldType {
    66  	return &FieldType{
    67  		tp:      tp,
    68  		flen:    UnspecifiedLength,
    69  		decimal: UnspecifiedLength,
    70  	}
    71  }
    72  
    73  // IsDecimalValid checks whether the decimal is valid.
    74  func (ft *FieldType) IsDecimalValid() bool {
    75  	if ft.GetType() == mysql.TypeNewDecimal && (ft.decimal < 0 || ft.decimal > mysql.MaxDecimalScale || ft.flen <= 0 || ft.flen > mysql.MaxDecimalWidth || ft.flen < ft.decimal) {
    76  		return false
    77  	}
    78  	return true
    79  }
    80  
    81  // IsVarLengthType Determine whether the column type is a variable-length type
    82  func (ft *FieldType) IsVarLengthType() bool {
    83  	switch ft.GetType() {
    84  	case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeJSON, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob:
    85  		return true
    86  	default:
    87  		return false
    88  	}
    89  }
    90  
    91  // GetType returns the type of the FieldType.
    92  func (ft *FieldType) GetType() byte {
    93  	if ft.array {
    94  		return mysql.TypeJSON
    95  	}
    96  	return ft.tp
    97  }
    98  
    99  // GetFlag returns the flag of the FieldType.
   100  func (ft *FieldType) GetFlag() uint {
   101  	return ft.flag
   102  }
   103  
   104  // GetFlen returns the length of the field.
   105  func (ft *FieldType) GetFlen() int {
   106  	return ft.flen
   107  }
   108  
   109  // GetDecimal returns the decimal of the FieldType.
   110  func (ft *FieldType) GetDecimal() int {
   111  	return ft.decimal
   112  }
   113  
   114  // GetCharset returns the field's charset
   115  func (ft *FieldType) GetCharset() string {
   116  	return ft.charset
   117  }
   118  
   119  // GetCollate returns the collation of the field.
   120  func (ft *FieldType) GetCollate() string {
   121  	return ft.collate
   122  }
   123  
   124  // GetElems returns the elements of the FieldType.
   125  func (ft *FieldType) GetElems() []string {
   126  	return ft.elems
   127  }
   128  
   129  // SetType sets the type of the FieldType.
   130  func (ft *FieldType) SetType(tp byte) {
   131  	ft.tp = tp
   132  	ft.array = false
   133  }
   134  
   135  // SetFlag sets the flag of the FieldType.
   136  func (ft *FieldType) SetFlag(flag uint) {
   137  	ft.flag = flag
   138  }
   139  
   140  // AddFlag adds a flag to the FieldType.
   141  func (ft *FieldType) AddFlag(flag uint) {
   142  	ft.flag |= flag
   143  }
   144  
   145  // AndFlag and the flag of the FieldType.
   146  func (ft *FieldType) AndFlag(flag uint) {
   147  	ft.flag &= flag
   148  }
   149  
   150  // ToggleFlag toggle the flag of the FieldType.
   151  func (ft *FieldType) ToggleFlag(flag uint) {
   152  	ft.flag ^= flag
   153  }
   154  
   155  // DelFlag delete the flag of the FieldType.
   156  func (ft *FieldType) DelFlag(flag uint) {
   157  	ft.flag &= ^flag
   158  }
   159  
   160  // SetFlen sets the length of the field.
   161  func (ft *FieldType) SetFlen(flen int) {
   162  	ft.flen = flen
   163  }
   164  
   165  // SetFlenUnderLimit sets the length of the field to the value of the argument
   166  func (ft *FieldType) SetFlenUnderLimit(flen int) {
   167  	if ft.GetType() == mysql.TypeNewDecimal {
   168  		ft.flen = mathutil.Min(flen, mysql.MaxDecimalWidth)
   169  	} else {
   170  		ft.flen = flen
   171  	}
   172  }
   173  
   174  // SetDecimal sets the decimal of the FieldType.
   175  func (ft *FieldType) SetDecimal(decimal int) {
   176  	ft.decimal = decimal
   177  }
   178  
   179  // SetDecimalUnderLimit sets the decimal of the field to the value of the argument
   180  func (ft *FieldType) SetDecimalUnderLimit(decimal int) {
   181  	if ft.GetType() == mysql.TypeNewDecimal {
   182  		ft.decimal = mathutil.Min(decimal, mysql.MaxDecimalScale)
   183  	} else {
   184  		ft.decimal = decimal
   185  	}
   186  }
   187  
   188  // UpdateFlenAndDecimalUnderLimit updates the length and decimal to the value of the argument
   189  func (ft *FieldType) UpdateFlenAndDecimalUnderLimit(old *FieldType, deltaDecimal int, deltaFlen int) {
   190  	if ft.GetType() != mysql.TypeNewDecimal {
   191  		return
   192  	}
   193  	if old.decimal < 0 {
   194  		deltaFlen += mysql.MaxDecimalScale
   195  		ft.decimal = mysql.MaxDecimalScale
   196  	} else {
   197  		ft.SetDecimal(old.decimal + deltaDecimal)
   198  	}
   199  	if old.flen < 0 {
   200  		ft.flen = mysql.MaxDecimalWidth
   201  	} else {
   202  		ft.SetFlenUnderLimit(old.flen + deltaFlen)
   203  	}
   204  }
   205  
   206  // SetCharset sets the charset of the FieldType.
   207  func (ft *FieldType) SetCharset(charset string) {
   208  	ft.charset = charset
   209  }
   210  
   211  // SetCollate sets the collation of the FieldType.
   212  func (ft *FieldType) SetCollate(collate string) {
   213  	ft.collate = collate
   214  }
   215  
   216  // SetElems sets the elements of the FieldType.
   217  func (ft *FieldType) SetElems(elems []string) {
   218  	ft.elems = elems
   219  }
   220  
   221  // SetElem sets the element of the FieldType.
   222  func (ft *FieldType) SetElem(idx int, element string) {
   223  	ft.elems[idx] = element
   224  }
   225  
   226  // SetArray sets the array field of the FieldType.
   227  func (ft *FieldType) SetArray(array bool) {
   228  	ft.array = array
   229  }
   230  
   231  // IsArray return true if the filed type is array.
   232  func (ft *FieldType) IsArray() bool {
   233  	return ft.array
   234  }
   235  
   236  // ArrayType return the type of the array.
   237  func (ft *FieldType) ArrayType() *FieldType {
   238  	if !ft.array {
   239  		return ft
   240  	}
   241  	clone := ft.Clone()
   242  	clone.SetArray(false)
   243  	return clone
   244  }
   245  
   246  // SetElemWithIsBinaryLit sets the element of the FieldType.
   247  func (ft *FieldType) SetElemWithIsBinaryLit(idx int, element string, isBinaryLit bool) {
   248  	ft.elems[idx] = element
   249  	if isBinaryLit {
   250  		// Create the binary literal flags lazily.
   251  		if ft.elemsIsBinaryLit == nil {
   252  			ft.elemsIsBinaryLit = make([]bool, len(ft.elems))
   253  		}
   254  		ft.elemsIsBinaryLit[idx] = true
   255  	}
   256  }
   257  
   258  // GetElem returns the element of the FieldType.
   259  func (ft *FieldType) GetElem(idx int) string {
   260  	return ft.elems[idx]
   261  }
   262  
   263  // GetElemIsBinaryLit returns the binary literal flag of the element at index idx.
   264  func (ft *FieldType) GetElemIsBinaryLit(idx int) bool {
   265  	if len(ft.elemsIsBinaryLit) == 0 {
   266  		return false
   267  	}
   268  	return ft.elemsIsBinaryLit[idx]
   269  }
   270  
   271  // CleanElemIsBinaryLit cleans the binary literal flag of the element at index idx.
   272  func (ft *FieldType) CleanElemIsBinaryLit() {
   273  	if ft != nil && ft.elemsIsBinaryLit != nil {
   274  		ft.elemsIsBinaryLit = nil
   275  	}
   276  }
   277  
   278  // Clone returns a copy of itself.
   279  func (ft *FieldType) Clone() *FieldType {
   280  	ret := *ft
   281  	return &ret
   282  }
   283  
   284  // Equal checks whether two FieldType objects are equal.
   285  func (ft *FieldType) Equal(other *FieldType) bool {
   286  	// We do not need to compare whole `ft.flag == other.flag` when wrapping cast upon an Expression.
   287  	// but need compare unsigned_flag of ft.flag.
   288  	// When tp is float or double with decimal unspecified, do not check whether flen is equal,
   289  	// because flen for them is useless.
   290  	// The decimal field can be ignored if the type is int or string.
   291  	tpEqual := (ft.GetType() == other.GetType()) || (ft.GetType() == mysql.TypeVarchar && other.GetType() == mysql.TypeVarString) || (ft.GetType() == mysql.TypeVarString && other.GetType() == mysql.TypeVarchar)
   292  	flenEqual := ft.flen == other.flen || (ft.EvalType() == ETReal && ft.decimal == UnspecifiedLength)
   293  	ignoreDecimal := ft.EvalType() == ETInt || ft.EvalType() == ETString
   294  	partialEqual := tpEqual &&
   295  		(ignoreDecimal || ft.decimal == other.decimal) &&
   296  		ft.charset == other.charset &&
   297  		ft.collate == other.collate &&
   298  		flenEqual &&
   299  		mysql.HasUnsignedFlag(ft.flag) == mysql.HasUnsignedFlag(other.flag)
   300  	if !partialEqual || len(ft.elems) != len(other.elems) {
   301  		return false
   302  	}
   303  	for i := range ft.elems {
   304  		if ft.elems[i] != other.elems[i] {
   305  			return false
   306  		}
   307  	}
   308  	return true
   309  }
   310  
   311  // PartialEqual checks whether two FieldType objects are equal.
   312  // If unsafe is true and the objects is string type, PartialEqual will ignore flen.
   313  // See https://github.com/pingcap/tidb/issues/35490#issuecomment-1211658886 for more detail.
   314  func (ft *FieldType) PartialEqual(other *FieldType, unsafe bool) bool {
   315  	if !unsafe || ft.EvalType() != ETString || other.EvalType() != ETString {
   316  		return ft.Equal(other)
   317  	}
   318  
   319  	partialEqual := ft.charset == other.charset && ft.collate == other.collate && mysql.HasUnsignedFlag(ft.flag) == mysql.HasUnsignedFlag(other.flag)
   320  	if !partialEqual || len(ft.elems) != len(other.elems) {
   321  		return false
   322  	}
   323  	for i := range ft.elems {
   324  		if ft.elems[i] != other.elems[i] {
   325  			return false
   326  		}
   327  	}
   328  	return true
   329  }
   330  
   331  // EvalType gets the type in evaluation.
   332  func (ft *FieldType) EvalType() EvalType {
   333  	switch ft.GetType() {
   334  	case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong,
   335  		mysql.TypeBit, mysql.TypeYear:
   336  		return ETInt
   337  	case mysql.TypeFloat, mysql.TypeDouble:
   338  		return ETReal
   339  	case mysql.TypeNewDecimal:
   340  		return ETDecimal
   341  	case mysql.TypeDate, mysql.TypeDatetime:
   342  		return ETDatetime
   343  	case mysql.TypeTimestamp:
   344  		return ETTimestamp
   345  	case mysql.TypeDuration:
   346  		return ETDuration
   347  	case mysql.TypeJSON:
   348  		return ETJson
   349  	case mysql.TypeEnum, mysql.TypeSet:
   350  		if ft.flag&mysql.EnumSetAsIntFlag > 0 {
   351  			return ETInt
   352  		}
   353  	}
   354  	return ETString
   355  }
   356  
   357  // Hybrid checks whether a type is a hybrid type, which can represent different types of value in specific context.
   358  func (ft *FieldType) Hybrid() bool {
   359  	return ft.GetType() == mysql.TypeEnum || ft.GetType() == mysql.TypeBit || ft.GetType() == mysql.TypeSet
   360  }
   361  
   362  // Init initializes the FieldType data.
   363  func (ft *FieldType) Init(tp byte) {
   364  	ft.tp = tp
   365  	ft.flen = UnspecifiedLength
   366  	ft.decimal = UnspecifiedLength
   367  }
   368  
   369  // CompactStr only considers tp/CharsetBin/flen/Deimal.
   370  // This is used for showing column type in infoschema.
   371  func (ft *FieldType) CompactStr() string {
   372  	ts := TypeToStr(ft.GetType(), ft.charset)
   373  	suffix := ""
   374  
   375  	defaultFlen, defaultDecimal := mysql.GetDefaultFieldLengthAndDecimal(ft.GetType())
   376  	isDecimalNotDefault := ft.decimal != defaultDecimal && ft.decimal != 0 && ft.decimal != UnspecifiedLength
   377  
   378  	// displayFlen and displayDecimal are flen and decimal values with `-1` substituted with default value.
   379  	displayFlen, displayDecimal := ft.flen, ft.decimal
   380  	if displayFlen == UnspecifiedLength {
   381  		displayFlen = defaultFlen
   382  	}
   383  	if displayDecimal == UnspecifiedLength {
   384  		displayDecimal = defaultDecimal
   385  	}
   386  
   387  	switch ft.GetType() {
   388  	case mysql.TypeEnum, mysql.TypeSet:
   389  		// Format is ENUM ('e1', 'e2') or SET ('e1', 'e2')
   390  		es := make([]string, 0, len(ft.elems))
   391  		for _, e := range ft.elems {
   392  			e = format.OutputFormat(e)
   393  			es = append(es, e)
   394  		}
   395  		suffix = fmt.Sprintf("('%s')", strings.Join(es, "','"))
   396  	case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDuration:
   397  		if isDecimalNotDefault {
   398  			suffix = fmt.Sprintf("(%d)", displayDecimal)
   399  		}
   400  	case mysql.TypeDouble, mysql.TypeFloat:
   401  		// 1. flen Not Default, decimal Not Default -> Valid
   402  		// 2. flen Not Default, decimal Default (-1) -> Invalid
   403  		// 3. flen Default, decimal Not Default -> Valid
   404  		// 4. flen Default, decimal Default -> Valid (hide)
   405  		if isDecimalNotDefault {
   406  			suffix = fmt.Sprintf("(%d,%d)", displayFlen, displayDecimal)
   407  		}
   408  	case mysql.TypeNewDecimal:
   409  		suffix = fmt.Sprintf("(%d,%d)", displayFlen, displayDecimal)
   410  	case mysql.TypeBit, mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString:
   411  		suffix = fmt.Sprintf("(%d)", displayFlen)
   412  	case mysql.TypeTiny:
   413  		// With display length deprecation active tinyint(1) still has
   414  		// a display length to indicate this might have been a BOOL.
   415  		// Connectors expect this.
   416  		//
   417  		// See also:
   418  		// https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-19.html
   419  		if !TiDBStrictIntegerDisplayWidth || (mysql.HasZerofillFlag(ft.flag) || displayFlen == 1) {
   420  			suffix = fmt.Sprintf("(%d)", displayFlen)
   421  		}
   422  	case mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong:
   423  		// Referring this issue #6688, the integer max display length is deprecated in MySQL 8.0.
   424  		// Since the length doesn't take any effect in TiDB storage or showing result, we remove it here.
   425  		if !TiDBStrictIntegerDisplayWidth || mysql.HasZerofillFlag(ft.flag) {
   426  			suffix = fmt.Sprintf("(%d)", displayFlen)
   427  		}
   428  	case mysql.TypeYear:
   429  		suffix = fmt.Sprintf("(%d)", ft.flen)
   430  	case mysql.TypeNull:
   431  		suffix = "(0)"
   432  	}
   433  	return ts + suffix
   434  }
   435  
   436  // InfoSchemaStr joins the CompactStr with unsigned flag and
   437  // returns a string.
   438  func (ft *FieldType) InfoSchemaStr() string {
   439  	suffix := ""
   440  	if mysql.HasUnsignedFlag(ft.flag) &&
   441  		ft.GetType() != mysql.TypeBit &&
   442  		ft.GetType() != mysql.TypeYear {
   443  		suffix = " unsigned"
   444  	}
   445  	return ft.CompactStr() + suffix
   446  }
   447  
   448  // String joins the information of FieldType and returns a string.
   449  // Note: when flen or decimal is unspecified, this function will use the default value instead of -1.
   450  func (ft *FieldType) String() string {
   451  	strs := []string{ft.CompactStr()}
   452  	if mysql.HasUnsignedFlag(ft.flag) {
   453  		strs = append(strs, "UNSIGNED")
   454  	}
   455  	if mysql.HasZerofillFlag(ft.flag) {
   456  		strs = append(strs, "ZEROFILL")
   457  	}
   458  	if mysql.HasBinaryFlag(ft.flag) && ft.GetType() != mysql.TypeString {
   459  		strs = append(strs, "BINARY")
   460  	}
   461  
   462  	if IsTypeChar(ft.GetType()) || IsTypeBlob(ft.GetType()) {
   463  		if ft.charset != "" && ft.charset != charset.CharsetBin {
   464  			strs = append(strs, fmt.Sprintf("CHARACTER SET %s", ft.charset))
   465  		}
   466  		if ft.collate != "" && ft.collate != charset.CharsetBin {
   467  			strs = append(strs, fmt.Sprintf("COLLATE %s", ft.collate))
   468  		}
   469  	}
   470  
   471  	return strings.Join(strs, " ")
   472  }
   473  
   474  // Restore implements Node interface.
   475  func (ft *FieldType) Restore(ctx *format.RestoreCtx) error {
   476  	ctx.WriteKeyWord(TypeToStr(ft.GetType(), ft.charset))
   477  
   478  	precision := UnspecifiedLength
   479  	scale := UnspecifiedLength
   480  
   481  	switch ft.GetType() {
   482  	case mysql.TypeEnum, mysql.TypeSet:
   483  		ctx.WritePlain("(")
   484  		for i, e := range ft.elems {
   485  			if i != 0 {
   486  				ctx.WritePlain(",")
   487  			}
   488  			ctx.WriteString(e)
   489  		}
   490  		ctx.WritePlain(")")
   491  	case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDuration:
   492  		precision = ft.decimal
   493  	case mysql.TypeUnspecified, mysql.TypeFloat, mysql.TypeDouble, mysql.TypeNewDecimal:
   494  		precision = ft.flen
   495  		scale = ft.decimal
   496  	default:
   497  		precision = ft.flen
   498  	}
   499  
   500  	if precision != UnspecifiedLength {
   501  		ctx.WritePlainf("(%d", precision)
   502  		if scale != UnspecifiedLength {
   503  			ctx.WritePlainf(",%d", scale)
   504  		}
   505  		ctx.WritePlain(")")
   506  	}
   507  
   508  	if mysql.HasUnsignedFlag(ft.flag) {
   509  		ctx.WriteKeyWord(" UNSIGNED")
   510  	}
   511  	if mysql.HasZerofillFlag(ft.flag) {
   512  		ctx.WriteKeyWord(" ZEROFILL")
   513  	}
   514  	if mysql.HasBinaryFlag(ft.flag) && ft.charset != charset.CharsetBin {
   515  		ctx.WriteKeyWord(" BINARY")
   516  	}
   517  
   518  	if IsTypeChar(ft.GetType()) || IsTypeBlob(ft.GetType()) {
   519  		if ft.charset != "" && ft.charset != charset.CharsetBin {
   520  			ctx.WriteKeyWord(" CHARACTER SET " + ft.charset)
   521  		}
   522  		if ft.collate != "" && ft.collate != charset.CharsetBin {
   523  			ctx.WriteKeyWord(" COLLATE ")
   524  			ctx.WritePlain(ft.collate)
   525  		}
   526  	}
   527  
   528  	return nil
   529  }
   530  
   531  // RestoreAsCastType is used for write AST back to string.
   532  func (ft *FieldType) RestoreAsCastType(ctx *format.RestoreCtx, explicitCharset bool) {
   533  	switch ft.tp {
   534  	case mysql.TypeVarString, mysql.TypeString:
   535  		skipWriteBinary := false
   536  		if ft.charset == charset.CharsetBin && ft.collate == charset.CollationBin {
   537  			ctx.WriteKeyWord("BINARY")
   538  			skipWriteBinary = true
   539  		} else {
   540  			ctx.WriteKeyWord("CHAR")
   541  		}
   542  		if ft.flen != UnspecifiedLength {
   543  			ctx.WritePlainf("(%d)", ft.flen)
   544  		}
   545  		if !explicitCharset {
   546  			break
   547  		}
   548  		if !skipWriteBinary && ft.flag&mysql.BinaryFlag != 0 {
   549  			ctx.WriteKeyWord(" BINARY")
   550  		}
   551  		if ft.charset != charset.CharsetBin && ft.charset != mysql.DefaultCharset {
   552  			ctx.WriteKeyWord(" CHARSET ")
   553  			ctx.WriteKeyWord(ft.charset)
   554  		}
   555  	case mysql.TypeDate:
   556  		ctx.WriteKeyWord("DATE")
   557  	case mysql.TypeDatetime:
   558  		ctx.WriteKeyWord("DATETIME")
   559  		if ft.decimal > 0 {
   560  			ctx.WritePlainf("(%d)", ft.decimal)
   561  		}
   562  	case mysql.TypeNewDecimal:
   563  		ctx.WriteKeyWord("DECIMAL")
   564  		if ft.flen > 0 && ft.decimal > 0 {
   565  			ctx.WritePlainf("(%d, %d)", ft.flen, ft.decimal)
   566  		} else if ft.flen > 0 {
   567  			ctx.WritePlainf("(%d)", ft.flen)
   568  		}
   569  	case mysql.TypeDuration:
   570  		ctx.WriteKeyWord("TIME")
   571  		if ft.decimal > 0 {
   572  			ctx.WritePlainf("(%d)", ft.decimal)
   573  		}
   574  	case mysql.TypeLonglong:
   575  		if ft.flag&mysql.UnsignedFlag != 0 {
   576  			ctx.WriteKeyWord("UNSIGNED")
   577  		} else {
   578  			ctx.WriteKeyWord("SIGNED")
   579  		}
   580  	case mysql.TypeJSON:
   581  		ctx.WriteKeyWord("JSON")
   582  	case mysql.TypeDouble:
   583  		ctx.WriteKeyWord("DOUBLE")
   584  	case mysql.TypeFloat:
   585  		ctx.WriteKeyWord("FLOAT")
   586  	case mysql.TypeYear:
   587  		ctx.WriteKeyWord("YEAR")
   588  	}
   589  	if ft.array {
   590  		ctx.WritePlain(" ")
   591  		ctx.WriteKeyWord("ARRAY")
   592  	}
   593  }
   594  
   595  // FormatAsCastType is used for write AST back to string.
   596  func (ft *FieldType) FormatAsCastType(w io.Writer, explicitCharset bool) {
   597  	var sb strings.Builder
   598  	restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags, &sb)
   599  	ft.RestoreAsCastType(restoreCtx, explicitCharset)
   600  	fmt.Fprint(w, sb.String())
   601  }
   602  
   603  // VarStorageLen indicates this column is a variable length column.
   604  const VarStorageLen = -1
   605  
   606  // StorageLength is the length of stored value for the type.
   607  func (ft *FieldType) StorageLength() int {
   608  	switch ft.GetType() {
   609  	case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong,
   610  		mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeFloat, mysql.TypeYear, mysql.TypeDuration,
   611  		mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp, mysql.TypeEnum, mysql.TypeSet,
   612  		mysql.TypeBit:
   613  		// This may not be the accurate length, because we may encode them as varint.
   614  		return 8
   615  	case mysql.TypeNewDecimal:
   616  		precision, frac := ft.flen-ft.decimal, ft.decimal
   617  		return precision/digitsPerWord*wordSize + dig2bytes[precision%digitsPerWord] + frac/digitsPerWord*wordSize + dig2bytes[frac%digitsPerWord]
   618  	default:
   619  		return VarStorageLen
   620  	}
   621  }
   622  
   623  // HasCharset indicates if a COLUMN has an associated charset. Returning false here prevents some information
   624  // statements(like `SHOW CREATE TABLE`) from attaching a CHARACTER SET clause to the column.
   625  func HasCharset(ft *FieldType) bool {
   626  	switch ft.GetType() {
   627  	case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeBlob,
   628  		mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob:
   629  		return !mysql.HasBinaryFlag(ft.flag)
   630  	case mysql.TypeEnum, mysql.TypeSet:
   631  		return true
   632  	}
   633  	return false
   634  }
   635  
   636  // for json
   637  type jsonFieldType struct {
   638  	Tp               byte
   639  	Flag             uint
   640  	Flen             int
   641  	Decimal          int
   642  	Charset          string
   643  	Collate          string
   644  	Elems            []string
   645  	ElemsIsBinaryLit []bool
   646  	Array            bool
   647  }
   648  
   649  // UnmarshalJSON implements the json.Unmarshaler interface.
   650  func (ft *FieldType) UnmarshalJSON(data []byte) error {
   651  	var r jsonFieldType
   652  	err := json.Unmarshal(data, &r)
   653  	if err == nil {
   654  		ft.tp = r.Tp
   655  		ft.flag = r.Flag
   656  		ft.flen = r.Flen
   657  		ft.decimal = r.Decimal
   658  		ft.charset = r.Charset
   659  		ft.collate = r.Collate
   660  		ft.elems = r.Elems
   661  		ft.elemsIsBinaryLit = r.ElemsIsBinaryLit
   662  		ft.array = r.Array
   663  	}
   664  	return err
   665  }
   666  
   667  // MarshalJSON marshals the FieldType to JSON.
   668  func (ft *FieldType) MarshalJSON() ([]byte, error) {
   669  	var r jsonFieldType
   670  	r.Tp = ft.tp
   671  	r.Flag = ft.flag
   672  	r.Flen = ft.flen
   673  	r.Decimal = ft.decimal
   674  	r.Charset = ft.charset
   675  	r.Collate = ft.collate
   676  	r.Elems = ft.elems
   677  	r.ElemsIsBinaryLit = ft.elemsIsBinaryLit
   678  	r.Array = ft.array
   679  	return json.Marshal(r)
   680  }
   681  
   682  const emptyFieldTypeSize = int64(unsafe.Sizeof(FieldType{}))
   683  
   684  // MemoryUsage return the memory usage of FieldType
   685  func (ft *FieldType) MemoryUsage() (sum int64) {
   686  	if ft == nil {
   687  		return
   688  	}
   689  	sum = emptyFieldTypeSize + int64(len(ft.charset)+len(ft.collate)) + int64(cap(ft.elems))*int64(unsafe.Sizeof(*new(string))) +
   690  		int64(cap(ft.elemsIsBinaryLit))*int64(unsafe.Sizeof(*new(bool)))
   691  
   692  	for _, s := range ft.elems {
   693  		sum += int64(len(s))
   694  	}
   695  	return
   696  }