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  }