github.com/XiaoMi/Gaea@v1.2.5/parser/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 "fmt" 18 "io" 19 "strings" 20 21 "github.com/XiaoMi/Gaea/mysql" 22 "github.com/XiaoMi/Gaea/parser/format" 23 ) 24 25 // UnspecifiedLength is unspecified length. 26 const ( 27 UnspecifiedLength = -1 28 ) 29 30 // FieldType records field type information. 31 type FieldType struct { 32 Tp byte 33 Flag uint 34 Flen int 35 Decimal int 36 Charset string 37 Collate string 38 // Elems is the element list for enum and set type. 39 Elems []string 40 } 41 42 // NewFieldType returns a FieldType, 43 // with a type and other information about field type. 44 func NewFieldType(tp byte) *FieldType { 45 return &FieldType{ 46 Tp: tp, 47 Flen: UnspecifiedLength, 48 Decimal: UnspecifiedLength, 49 } 50 } 51 52 // Clone returns a copy of itself. 53 func (ft *FieldType) Clone() *FieldType { 54 ret := *ft 55 return &ret 56 } 57 58 // Equal checks whether two FieldType objects are equal. 59 func (ft *FieldType) Equal(other *FieldType) bool { 60 // We do not need to compare whole `ft.Flag == other.Flag` when wrapping cast upon an Expression. 61 // but need compare unsigned_flag of ft.Flag. 62 partialEqual := ft.Tp == other.Tp && 63 ft.Flen == other.Flen && 64 ft.Decimal == other.Decimal && 65 ft.Charset == other.Charset && 66 ft.Collate == other.Collate && 67 mysql.HasUnsignedFlag(ft.Flag) == mysql.HasUnsignedFlag(other.Flag) 68 if !partialEqual || len(ft.Elems) != len(other.Elems) { 69 return false 70 } 71 for i := range ft.Elems { 72 if ft.Elems[i] != other.Elems[i] { 73 return false 74 } 75 } 76 return true 77 } 78 79 // EvalType gets the type in evaluation. 80 func (ft *FieldType) EvalType() EvalType { 81 switch ft.Tp { 82 case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, 83 mysql.TypeBit, mysql.TypeYear: 84 return ETInt 85 case mysql.TypeFloat, mysql.TypeDouble: 86 return ETReal 87 case mysql.TypeNewDecimal: 88 return ETDecimal 89 case mysql.TypeDate, mysql.TypeDatetime: 90 return ETDatetime 91 case mysql.TypeTimestamp: 92 return ETTimestamp 93 case mysql.TypeDuration: 94 return ETDuration 95 case mysql.TypeJSON: 96 return ETJson 97 } 98 return ETString 99 } 100 101 // Hybrid checks whether a type is a hybrid type, which can represent different types of value in specific context. 102 func (ft *FieldType) Hybrid() bool { 103 return ft.Tp == mysql.TypeEnum || ft.Tp == mysql.TypeBit || ft.Tp == mysql.TypeSet 104 } 105 106 // Init initializes the FieldType data. 107 func (ft *FieldType) Init(tp byte) { 108 ft.Tp = tp 109 ft.Flen = UnspecifiedLength 110 ft.Decimal = UnspecifiedLength 111 } 112 113 // CompactStr only considers Tp/CharsetBin/Flen/Deimal. 114 // This is used for showing column type in infoschema. 115 func (ft *FieldType) CompactStr() string { 116 ts := TypeToStr(ft.Tp, ft.Charset) 117 suffix := "" 118 119 defaultFlen, defaultDecimal := mysql.GetDefaultFieldLengthAndDecimal(ft.Tp) 120 isDecimalNotDefault := ft.Decimal != defaultDecimal && ft.Decimal != 0 && ft.Decimal != UnspecifiedLength 121 122 // displayFlen and displayDecimal are flen and decimal values with `-1` substituted with default value. 123 displayFlen, displayDecimal := ft.Flen, ft.Decimal 124 if displayFlen == 0 || displayFlen == UnspecifiedLength { 125 displayFlen = defaultFlen 126 } 127 if displayDecimal == 0 || displayDecimal == UnspecifiedLength { 128 displayDecimal = defaultDecimal 129 } 130 131 switch ft.Tp { 132 case mysql.TypeEnum, mysql.TypeSet: 133 // Format is ENUM ('e1', 'e2') or SET ('e1', 'e2') 134 es := make([]string, 0, len(ft.Elems)) 135 for _, e := range ft.Elems { 136 e = format.OutputFormat(e) 137 es = append(es, e) 138 } 139 suffix = fmt.Sprintf("('%s')", strings.Join(es, "','")) 140 case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDuration: 141 if isDecimalNotDefault { 142 suffix = fmt.Sprintf("(%d)", displayDecimal) 143 } 144 case mysql.TypeDouble, mysql.TypeFloat: 145 // 1. Flen Not Default, Decimal Not Default -> Valid 146 // 2. Flen Not Default, Decimal Default (-1) -> Invalid 147 // 3. Flen Default, Decimal Not Default -> Valid 148 // 4. Flen Default, Decimal Default -> Valid (hide) 149 if isDecimalNotDefault { 150 suffix = fmt.Sprintf("(%d,%d)", displayFlen, displayDecimal) 151 } 152 case mysql.TypeNewDecimal: 153 suffix = fmt.Sprintf("(%d,%d)", displayFlen, displayDecimal) 154 case mysql.TypeBit, mysql.TypeShort, mysql.TypeTiny, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString: 155 // Flen is always shown. 156 suffix = fmt.Sprintf("(%d)", displayFlen) 157 } 158 return ts + suffix 159 } 160 161 // InfoSchemaStr joins the CompactStr with unsigned flag and 162 // returns a string. 163 func (ft *FieldType) InfoSchemaStr() string { 164 suffix := "" 165 if mysql.HasUnsignedFlag(ft.Flag) { 166 suffix = " unsigned" 167 } 168 return ft.CompactStr() + suffix 169 } 170 171 // String joins the information of FieldType and returns a string. 172 // Note: when flen or decimal is unspecified, this function will use the default value instead of -1. 173 func (ft *FieldType) String() string { 174 strs := []string{ft.CompactStr()} 175 if mysql.HasUnsignedFlag(ft.Flag) { 176 strs = append(strs, "UNSIGNED") 177 } 178 if mysql.HasZerofillFlag(ft.Flag) { 179 strs = append(strs, "ZEROFILL") 180 } 181 if mysql.HasBinaryFlag(ft.Flag) && ft.Tp != mysql.TypeString { 182 strs = append(strs, "BINARY") 183 } 184 185 if IsTypeChar(ft.Tp) || IsTypeBlob(ft.Tp) { 186 if ft.Charset != "" && ft.Charset != mysql.CharsetBin { 187 strs = append(strs, fmt.Sprintf("CHARACTER SET %s", ft.Charset)) 188 } 189 if ft.Collate != "" && ft.Collate != mysql.CharsetBin { 190 strs = append(strs, fmt.Sprintf("COLLATE %s", ft.Collate)) 191 } 192 } 193 194 return strings.Join(strs, " ") 195 } 196 197 // Restore implements Node interface. 198 func (ft *FieldType) Restore(ctx *format.RestoreCtx) error { 199 ctx.WriteKeyWord(TypeToStr(ft.Tp, ft.Charset)) 200 201 switch ft.Tp { 202 case mysql.TypeEnum, mysql.TypeSet: 203 ctx.WritePlain("(") 204 for i, e := range ft.Elems { 205 if i != 0 { 206 ctx.WritePlain(",") 207 } 208 ctx.WriteString(e) 209 } 210 ctx.WritePlain(")") 211 case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDuration: 212 if ft.Flen > 0 && ft.Decimal > 0 { 213 ctx.WritePlainf("(%d)", ft.Decimal) 214 } 215 case mysql.TypeDouble, mysql.TypeFloat: 216 if ft.Flen > 0 && ft.Decimal > 0 { 217 ctx.WritePlainf("(%d,%d)", ft.Flen, ft.Decimal) 218 } 219 case mysql.TypeNewDecimal: 220 if ft.Flen > 0 && ft.Decimal > 0 { 221 ctx.WritePlainf("(%d,%d)", ft.Flen, ft.Decimal) 222 } 223 case mysql.TypeBit, mysql.TypeShort, mysql.TypeTiny, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeBlob, mysql.TypeLongBlob, mysql.TypeYear: 224 if ft.Flen > 0 { 225 ctx.WritePlainf("(%d)", ft.Flen) 226 } 227 } 228 229 if mysql.HasUnsignedFlag(ft.Flag) { 230 ctx.WriteKeyWord(" UNSIGNED") 231 } 232 if mysql.HasZerofillFlag(ft.Flag) { 233 ctx.WriteKeyWord(" ZEROFILL") 234 } 235 if mysql.HasBinaryFlag(ft.Flag) && ft.Charset != mysql.CharsetBin { 236 ctx.WriteKeyWord(" BINARY") 237 } 238 239 if IsTypeChar(ft.Tp) || IsTypeBlob(ft.Tp) { 240 if ft.Charset != "" && ft.Charset != mysql.CharsetBin { 241 ctx.WriteKeyWord(" CHARACTER SET " + ft.Charset) 242 } 243 if ft.Collate != "" && ft.Collate != mysql.CharsetBin { 244 ctx.WriteKeyWord(" COLLATE ") 245 ctx.WritePlain(ft.Collate) 246 } 247 } 248 249 return nil 250 } 251 252 // FormatAsCastType is used for write AST back to string. 253 func (ft *FieldType) FormatAsCastType(w io.Writer) { 254 switch ft.Tp { 255 case mysql.TypeVarString: 256 if ft.Charset == mysql.CharsetBin && ft.Collate == mysql.CollationBin { 257 fmt.Fprint(w, "BINARY") 258 } else { 259 fmt.Fprint(w, "CHAR") 260 } 261 if ft.Flen != UnspecifiedLength { 262 fmt.Fprintf(w, "(%d)", ft.Flen) 263 } 264 if ft.Flag&mysql.BinaryFlag != 0 { 265 fmt.Fprint(w, " BINARY") 266 } 267 if ft.Charset != mysql.CharsetBin && ft.Charset != mysql.DefaultCharset { 268 fmt.Fprintf(w, " CHARACTER SET %s", ft.Charset) 269 } 270 case mysql.TypeDate: 271 fmt.Fprint(w, "DATE") 272 case mysql.TypeDatetime: 273 fmt.Fprint(w, "DATETIME") 274 if ft.Decimal > 0 { 275 fmt.Fprintf(w, "(%d)", ft.Decimal) 276 } 277 case mysql.TypeNewDecimal: 278 fmt.Fprint(w, "DECIMAL") 279 if ft.Flen > 0 && ft.Decimal > 0 { 280 fmt.Fprintf(w, "(%d, %d)", ft.Flen, ft.Decimal) 281 } else if ft.Flen > 0 { 282 fmt.Fprintf(w, "(%d)", ft.Flen) 283 } 284 case mysql.TypeDuration: 285 fmt.Fprint(w, "TIME") 286 if ft.Decimal > 0 { 287 fmt.Fprintf(w, "(%d)", ft.Decimal) 288 } 289 case mysql.TypeLonglong: 290 if ft.Flag&mysql.UnsignedFlag != 0 { 291 fmt.Fprint(w, "UNSIGNED") 292 } else { 293 fmt.Fprint(w, "SIGNED") 294 } 295 case mysql.TypeJSON: 296 fmt.Fprint(w, "JSON") 297 } 298 } 299 300 // VarStorageLen indicates this column is a variable length column. 301 const VarStorageLen = -1 302 303 // StorageLength is the length of stored value for the type. 304 func (ft *FieldType) StorageLength() int { 305 switch ft.Tp { 306 case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, 307 mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeFloat, mysql.TypeYear, mysql.TypeDuration, 308 mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp, mysql.TypeEnum, mysql.TypeSet, 309 mysql.TypeBit: 310 // This may not be the accurate length, because we may encode them as varint. 311 return 8 312 case mysql.TypeNewDecimal: 313 precision, frac := ft.Flen-ft.Decimal, ft.Decimal 314 return precision/digitsPerWord*wordSize + dig2bytes[precision%digitsPerWord] + frac/digitsPerWord*wordSize + dig2bytes[frac%digitsPerWord] 315 default: 316 return VarStorageLen 317 } 318 }