github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/memstore/common/data_type.go (about) 1 // Copyright (c) 2017-2018 Uber Technologies, 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package common 16 17 import ( 18 "bytes" 19 "fmt" 20 "github.com/satori/go.uuid" 21 "math" 22 "reflect" 23 "regexp" 24 "strconv" 25 "strings" 26 "unsafe" 27 28 metaCom "github.com/uber/aresdb/metastore/common" 29 "github.com/uber/aresdb/utils" 30 ) 31 32 // DataType is the type of value supported in gforcedb. 33 type DataType uint32 34 35 // The list of supported DataTypes. 36 // DataType & 0x0000FFFF: The width of the data type in bits. 37 // DataType & 0x00FF0000 >> 16: The base type of the enum. 38 // DataType & 0xFF000000 >> 24: Reserved for supporting variable length values (array). 39 // See https://github.com/uber/aresdb/wiki/redologs for more details. 40 const ( 41 Unknown DataType = 0x00000000 42 Bool DataType = 0x00000001 43 Int8 DataType = 0x00010008 44 Uint8 DataType = 0x00020008 45 Int16 DataType = 0x00030010 46 Uint16 DataType = 0x00040010 47 Int32 DataType = 0x00050020 48 Uint32 DataType = 0x00060020 49 Float32 DataType = 0x00070020 50 SmallEnum DataType = 0x00080008 51 BigEnum DataType = 0x00090010 52 UUID DataType = 0x000a0080 53 GeoPoint DataType = 0x000b0040 54 GeoShape DataType = 0x000c0000 55 Int64 DataType = 0x000d0040 56 ) 57 58 // DataTypeName returns the literal name of the data type. 59 var DataTypeName = map[DataType]string{ 60 Unknown: "Unknown", 61 Bool: metaCom.Bool, 62 Int8: metaCom.Int8, 63 Uint8: metaCom.Uint8, 64 Int16: metaCom.Int16, 65 Uint16: metaCom.Uint16, 66 Int32: metaCom.Int32, 67 Uint32: metaCom.Uint32, 68 Float32: metaCom.Float32, 69 SmallEnum: metaCom.SmallEnum, 70 BigEnum: metaCom.BigEnum, 71 UUID: metaCom.UUID, 72 GeoPoint: metaCom.GeoPoint, 73 GeoShape: metaCom.GeoShape, 74 Int64: metaCom.Int64, 75 } 76 77 // StringToDataType maps string representation to DataType 78 var StringToDataType = map[string]DataType{ 79 metaCom.Bool: Bool, 80 metaCom.Int8: Int8, 81 metaCom.Uint8: Uint8, 82 metaCom.Int16: Int16, 83 metaCom.Uint16: Uint16, 84 metaCom.Int32: Int32, 85 metaCom.Uint32: Uint32, 86 metaCom.Float32: Float32, 87 metaCom.SmallEnum: SmallEnum, 88 metaCom.BigEnum: BigEnum, 89 metaCom.UUID: UUID, 90 metaCom.GeoPoint: GeoPoint, 91 metaCom.GeoShape: GeoShape, 92 metaCom.Int64: Int64, 93 } 94 95 // NewDataType converts an uint32 value into a DataType. It returns error if the the data type is 96 // invalid. 97 func NewDataType(value uint32) (DataType, error) { 98 ret := DataType(value) 99 switch ret { 100 case Bool: 101 case Int8: 102 case Uint8: 103 case Int16: 104 case Uint16: 105 case Int32: 106 case Uint32: 107 case Int64: 108 case Float32: 109 case SmallEnum: 110 case BigEnum: 111 case UUID: 112 case GeoPoint: 113 case GeoShape: 114 default: 115 return Unknown, utils.StackError(nil, "Invalid data type value %#x", value) 116 } 117 return ret, nil 118 } 119 120 // IsNumeric determines whether a data type is numeric 121 func IsNumeric(dataType DataType) bool { 122 return (dataType >= Int8 && dataType <= Float32) || dataType == Int64 123 } 124 125 // DataTypeBits returns the number of bits of a data type. 126 func DataTypeBits(dataType DataType) int { 127 return int(0x0000FFFF & dataType) 128 } 129 130 // DataTypeForColumn returns the in memory data type for a column 131 func DataTypeForColumn(column metaCom.Column) DataType { 132 dataType := DataTypeFromString(column.Type) 133 if column.HLLConfig.IsHLLColumn { 134 return Uint32 135 } 136 return dataType 137 } 138 139 // DataTypeFromString convert string representation of data type into DataType 140 func DataTypeFromString(str string) DataType { 141 if dataType, exist := StringToDataType[str]; exist { 142 return dataType 143 } 144 return Unknown 145 } 146 147 // DataTypeBytes returns how many bytes a value of the data type occupies. 148 func DataTypeBytes(dataType DataType) int { 149 return (DataTypeBits(dataType) + 7) / 8 150 } 151 152 // ConvertValueForType converts data value based on data type 153 func ConvertValueForType(dataType DataType, value interface{}) (interface{}, error) { 154 ok := false 155 var out interface{} 156 switch dataType { 157 case Bool: 158 out, ok = ConvertToBool(value) 159 case SmallEnum: 160 fallthrough 161 case Uint8: 162 out, ok = ConvertToUint8(value) 163 case Int8: 164 out, ok = ConvertToInt8(value) 165 case Int16: 166 out, ok = ConvertToInt16(value) 167 case BigEnum: 168 fallthrough 169 case Uint16: 170 out, ok = ConvertToUint16(value) 171 case Uint32: 172 out, ok = ConvertToUint32(value) 173 case Int32: 174 out, ok = ConvertToInt32(value) 175 case Int64: 176 out, ok = ConvertToInt64(value) 177 case Float32: 178 out, ok = ConvertToFloat32(value) 179 case UUID: 180 out, ok = ConvertToUUID(value) 181 case GeoPoint: 182 out, ok = ConvertToGeoPoint(value) 183 case GeoShape: 184 out, ok = ConvertToGeoShape(value) 185 } 186 if !ok { 187 return nil, utils.StackError(nil, "Invalid data value %v for data type %s", value, DataTypeName[dataType]) 188 } 189 return out, nil 190 } 191 192 // ConvertToBool convert input into bool at best effort 193 func ConvertToBool(value interface{}) (bool, bool) { 194 if v, ok := value.(bool); ok { 195 return v, ok 196 } 197 198 // try converting "true" "false" 199 if v, ok := value.(string); ok { 200 if strings.ToLower(v) == "true" { 201 return true, true 202 } else if strings.ToLower(v) == "false" { 203 return false, true 204 } 205 } 206 207 // try convert as number 208 v, ok := ConvertToInt8(value) 209 if ok { 210 if v == 1 { 211 return true, true 212 } else if v == 0 { 213 return false, true 214 } 215 } 216 217 return false, false 218 } 219 220 // ConvertToInt8 convert input into int8 at best effort 221 func ConvertToInt8(value interface{}) (int8, bool) { 222 if v, ok := ConvertToInt64(value); ok { 223 if !reflect.ValueOf(int8(0)).OverflowInt(v) { 224 return int8(v), ok 225 } 226 } 227 return 0, false 228 } 229 230 // ConvertToUint8 convert input into uint8 at best effort 231 func ConvertToUint8(value interface{}) (uint8, bool) { 232 if v, ok := ConvertToUint64(value); ok { 233 if !reflect.ValueOf(uint8(0)).OverflowUint(v) { 234 return uint8(v), ok 235 } 236 } 237 return 0, false 238 } 239 240 // ConvertToInt16 convert input into int16 at best effort 241 func ConvertToInt16(value interface{}) (int16, bool) { 242 if v, ok := ConvertToInt64(value); ok { 243 if !reflect.ValueOf(int16(0)).OverflowInt(v) { 244 return int16(v), ok 245 } 246 } 247 return 0, false 248 } 249 250 // ConvertToUint16 convert input into uint16 at best effort 251 func ConvertToUint16(value interface{}) (uint16, bool) { 252 if v, ok := ConvertToUint64(value); ok { 253 if !reflect.ValueOf(uint16(0)).OverflowUint(v) { 254 return uint16(v), ok 255 } 256 } 257 return 0, false 258 } 259 260 // ConvertToInt32 convert input into int32 at best effort 261 func ConvertToInt32(value interface{}) (int32, bool) { 262 if v, ok := ConvertToInt64(value); ok { 263 if !reflect.ValueOf(int32(0)).OverflowInt(v) { 264 return int32(v), ok 265 } 266 } 267 return 0, false 268 } 269 270 // ConvertToUint32 convert input into uint32 at best effort 271 func ConvertToUint32(value interface{}) (uint32, bool) { 272 if v, ok := ConvertToUint64(value); ok { 273 if !reflect.ValueOf(uint32(0)).OverflowUint(v) { 274 return uint32(v), ok 275 } 276 } 277 return 0, false 278 } 279 280 // ConvertToFloat32 convert input into float32 at best effort 281 func ConvertToFloat32(value interface{}) (float32, bool) { 282 if v, ok := ConvertToFloat64(value); ok { 283 if !reflect.ValueOf(float32(0)).OverflowFloat(v) { 284 return float32(v), true 285 } 286 } 287 return 0, false 288 } 289 290 // ConvertToUint64 convert input into uint64 at best effort 291 func ConvertToUint64(value interface{}) (uint64, bool) { 292 switch v := value.(type) { 293 case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64: 294 num := reflect.ValueOf(value).Convert(reflect.TypeOf(uint64(0))).Uint() 295 return num, true 296 case string: 297 num, err := strconv.ParseUint(v, 10, 64) 298 if err == nil { 299 return num, true 300 } 301 } 302 return 0, false 303 } 304 305 // ConvertToInt64 convert input into int64 at best effort 306 func ConvertToInt64(value interface{}) (int64, bool) { 307 switch v := value.(type) { 308 case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64: 309 num := reflect.ValueOf(value).Convert(reflect.TypeOf(int64(0))).Int() 310 return num, true 311 case string: 312 num, err := strconv.ParseInt(v, 10, 64) 313 if err == nil { 314 return num, true 315 } 316 } 317 return 0, false 318 } 319 320 // ConvertToFloat64 convert input into float64 at best effort 321 func ConvertToFloat64(value interface{}) (float64, bool) { 322 switch v := value.(type) { 323 case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64: 324 num := reflect.ValueOf(value).Convert(reflect.TypeOf(float64(0))).Float() 325 return num, !math.IsInf(num, 0) && !math.IsNaN(num) 326 case string: 327 num, err := strconv.ParseFloat(v, 64) 328 if err == nil { 329 return num, true 330 } 331 } 332 return float64(0), false 333 } 334 335 // ConvertToUUID convert input into uuid type ([2]uint64) at best effort 336 func ConvertToUUID(value interface{}) ([2]uint64, bool) { 337 switch v := value.(type) { 338 case [2]uint64: 339 return v, true 340 case []byte: 341 if len(v) == 16 { 342 return *(*[2]uint64)(unsafe.Pointer(&v[0])), true 343 } 344 return [2]uint64{}, false 345 case string: 346 u, err := uuid.FromString(string(v)) 347 if err != nil { 348 return [2]uint64{}, false 349 } 350 bytes := u.Bytes() 351 return *(*[2]uint64)(unsafe.Pointer(&bytes[0])), true 352 } 353 354 return [2]uint64{}, false 355 } 356 357 // GeoPointFromString convert string to geopoint 358 // we support wkt format, eg. Point(lng,lat) 359 // Inside gforcedb system we store lat,lng format 360 func GeoPointFromString(str string) (point [2]float32, err error) { 361 lngLatStrs := strings.Fields(strings.NewReplacer("p", "", "o", "", "i", "", "n", "", "t", "", "(", "", ")", "", ",", " ").Replace(strings.ToLower(str))) 362 if len(lngLatStrs) != 2 { 363 err = fmt.Errorf("invalid point, requires format: Point(lng,lat), got %s", str) 364 return 365 } 366 367 var lng, lat float64 368 lng, err = strconv.ParseFloat(lngLatStrs[0], 32) 369 if err != nil || lng < -180 || lng > 180 { 370 err = utils.StackError(err, "invalid point, longitude should be float number in [-180, 180], got %s", lngLatStrs[0]) 371 return 372 } 373 lat, err = strconv.ParseFloat(lngLatStrs[1], 32) 374 if err != nil || lat < -90 || lat > 90 { 375 err = utils.StackError(err, "invalid point, latitude should be float number in [-90, 90], got %s", lngLatStrs[1]) 376 return 377 } 378 return [2]float32{float32(lat), float32(lng)}, nil 379 } 380 381 // ConvertToGeoPoint convert input into uuid type ([2]float32) at best effort 382 func ConvertToGeoPoint(value interface{}) ([2]float32, bool) { 383 switch v := value.(type) { 384 case string: 385 point, err := GeoPointFromString(v) 386 if err == nil { 387 return point, true 388 } 389 case [2]float32: 390 return v, true 391 case [2]float64: 392 return [2]float32{float32(v[0]), float32(v[1])}, true 393 } 394 return [2]float32{}, false 395 } 396 397 // GeoShapeFromString convert string to geoshape 398 // Supported format POLYGON ((lng lat, lng lat, lng lat, ...), (...)) 399 func GeoShapeFromString(str string) (GeoShapeGo, error) { 400 charsToTrim := "polygon() " 401 polygonStrs := regexp.MustCompile(`\),\s*\(`).Split(strings.TrimFunc(strings.ToLower(str), func(r rune) bool { 402 return strings.IndexRune(charsToTrim, r) >= 0 403 }), -1) 404 405 shape := GeoShapeGo{} 406 shape.Polygons = make([][]GeoPointGo, 0, len(polygonStrs)) 407 for _, polygonStr := range polygonStrs { 408 lngLatPairs := strings.Split(polygonStr, ",") 409 polygon := make([]GeoPointGo, 0, len(lngLatPairs)) 410 for _, lngLatPair := range lngLatPairs { 411 lngLat := strings.Fields(lngLatPair) 412 if len(lngLat) != 2 { 413 return GeoShapeGo{}, utils.StackError(nil, "invalid point format %s", lngLatPair) 414 } 415 lng, err := strconv.ParseFloat(lngLat[0], 32) 416 if err != nil || lng < -180 || lng > 180 { 417 return GeoShapeGo{}, utils.StackError(err, "invalid longitude, expect float number in [-180, 180], got %s", lngLat[0]) 418 } 419 lat, err := strconv.ParseFloat(lngLat[1], 32) 420 if err != nil || lat < -90 || lat > 90 { 421 return GeoShapeGo{}, utils.StackError(err, "invalid latitude, expect float number in [-90, 90], got %s", lngLat[1]) 422 } 423 point := GeoPointGo{float32(lat), float32(lng)} 424 polygon = append(polygon, point) 425 } 426 shape.Polygons = append(shape.Polygons, polygon) 427 } 428 return shape, nil 429 } 430 431 // ConvertToGeoShape converts the arbitrary value to GeoShapeGo 432 func ConvertToGeoShape(value interface{}) (*GeoShapeGo, bool) { 433 switch v := value.(type) { 434 case string: 435 shape, err := GeoShapeFromString(v) 436 if err == nil { 437 return &shape, true 438 } 439 case []byte: 440 shape := GeoShapeGo{} 441 dataReader := utils.NewStreamDataReader(bytes.NewReader(v)) 442 err := shape.Read(&dataReader) 443 if err == nil { 444 return &shape, true 445 } 446 } 447 return nil, false 448 } 449 450 // IsGoType determines whether a data type is golang type 451 func IsGoType(dataType DataType) bool { 452 // for now we only have GeoShape 453 return dataType == GeoShape 454 } 455 456 // IsEnumType determines whether a data type is enum type 457 func IsEnumType(dataType DataType) bool { 458 return dataType == SmallEnum || dataType == BigEnum 459 } 460 461 // GetGoDataValue return GoDataValue 462 func GetGoDataValue(dataType DataType) GoDataValue { 463 switch dataType { 464 case GeoShape: 465 return &GeoShapeGo{} 466 } 467 return nil 468 }