diff --git a/pkg/tsdb/sql_engine.go b/pkg/tsdb/sql_engine.go index 56ed2cd3cb6..274e5b05dc1 100644 --- a/pkg/tsdb/sql_engine.go +++ b/pkg/tsdb/sql_engine.go @@ -2,9 +2,12 @@ package tsdb import ( "context" + "fmt" "sync" "time" + "github.com/grafana/grafana/pkg/components/null" + "github.com/go-xorm/core" "github.com/go-xorm/xorm" "github.com/grafana/grafana/pkg/components/simplejson" @@ -185,3 +188,109 @@ func ConvertSqlTimeColumnToEpochMs(values RowValues, timeIndex int) { } } } + +// ConvertSqlValueColumnToFloat converts timeseries value column to float. +func ConvertSqlValueColumnToFloat(columnName string, columnValue interface{}) (null.Float, error) { + var value null.Float + + switch typedValue := columnValue.(type) { + case int: + value = null.FloatFrom(float64(typedValue)) + case *int: + if typedValue == nil { + value.Valid = false + } else { + value = null.FloatFrom(float64(*typedValue)) + } + case int64: + value = null.FloatFrom(float64(typedValue)) + case *int64: + if typedValue == nil { + value.Valid = false + } else { + value = null.FloatFrom(float64(*typedValue)) + } + case int32: + value = null.FloatFrom(float64(typedValue)) + case *int32: + if typedValue == nil { + value.Valid = false + } else { + value = null.FloatFrom(float64(*typedValue)) + } + case int16: + value = null.FloatFrom(float64(typedValue)) + case *int16: + if typedValue == nil { + value.Valid = false + } else { + value = null.FloatFrom(float64(*typedValue)) + } + case int8: + value = null.FloatFrom(float64(typedValue)) + case *int8: + if typedValue == nil { + value.Valid = false + } else { + value = null.FloatFrom(float64(*typedValue)) + } + case uint: + value = null.FloatFrom(float64(typedValue)) + case *uint: + if typedValue == nil { + value.Valid = false + } else { + value = null.FloatFrom(float64(*typedValue)) + } + case uint64: + value = null.FloatFrom(float64(typedValue)) + case *uint64: + if typedValue == nil { + value.Valid = false + } else { + value = null.FloatFrom(float64(*typedValue)) + } + case uint32: + value = null.FloatFrom(float64(typedValue)) + case *uint32: + if typedValue == nil { + value.Valid = false + } else { + value = null.FloatFrom(float64(*typedValue)) + } + case uint16: + value = null.FloatFrom(float64(typedValue)) + case *uint16: + if typedValue == nil { + value.Valid = false + } else { + value = null.FloatFrom(float64(*typedValue)) + } + case uint8: + value = null.FloatFrom(float64(typedValue)) + case *uint8: + if typedValue == nil { + value.Valid = false + } else { + value = null.FloatFrom(float64(*typedValue)) + } + case float64: + value = null.FloatFrom(typedValue) + case *float64: + value = null.FloatFromPtr(typedValue) + case float32: + value = null.FloatFrom(float64(typedValue)) + case *float32: + if typedValue == nil { + value.Valid = false + } else { + value = null.FloatFrom(float64(*typedValue)) + } + case nil: + value.Valid = false + default: + return null.NewFloat(0, false), fmt.Errorf("Value column must have numeric datatype, column: %s type: %T value: %v", columnName, typedValue, typedValue) + } + + return value, nil +} diff --git a/pkg/tsdb/sql_engine_test.go b/pkg/tsdb/sql_engine_test.go index 4c6951a0196..ce1fb45de21 100644 --- a/pkg/tsdb/sql_engine_test.go +++ b/pkg/tsdb/sql_engine_test.go @@ -1,10 +1,11 @@ package tsdb import ( - "fmt" "testing" "time" + "github.com/grafana/grafana/pkg/components/null" + . "github.com/smartystreets/goconvey/convey" ) @@ -156,8 +157,6 @@ func TestSqlEngine(t *testing.T) { So(fixtures[1].(float64), ShouldEqual, tMilliseconds) So(fixtures[2].(float64), ShouldEqual, tMilliseconds) So(fixtures[3].(float64), ShouldEqual, tMilliseconds) - fmt.Println(fixtures[4].(float64)) - fmt.Println(tMilliseconds) So(fixtures[4].(float64), ShouldEqual, tMilliseconds) So(fixtures[5].(float64), ShouldEqual, tMilliseconds) So(fixtures[6], ShouldBeNil) @@ -183,5 +182,101 @@ func TestSqlEngine(t *testing.T) { So(fixtures[2], ShouldBeNil) }) }) + + Convey("Given row with value columns", func() { + intValue := 1 + int64Value := int64(1) + int32Value := int32(1) + int16Value := int16(1) + int8Value := int8(1) + float64Value := float64(1) + float32Value := float32(1) + uintValue := uint(1) + uint64Value := uint64(1) + uint32Value := uint32(1) + uint16Value := uint16(1) + uint8Value := uint8(1) + + fixtures := make([]interface{}, 24) + fixtures[0] = intValue + fixtures[1] = &intValue + fixtures[2] = int64Value + fixtures[3] = &int64Value + fixtures[4] = int32Value + fixtures[5] = &int32Value + fixtures[6] = int16Value + fixtures[7] = &int16Value + fixtures[8] = int8Value + fixtures[9] = &int8Value + fixtures[10] = float64Value + fixtures[11] = &float64Value + fixtures[12] = float32Value + fixtures[13] = &float32Value + fixtures[14] = uintValue + fixtures[15] = &uintValue + fixtures[16] = uint64Value + fixtures[17] = &uint64Value + fixtures[18] = uint32Value + fixtures[19] = &uint32Value + fixtures[20] = uint16Value + fixtures[21] = &uint16Value + fixtures[22] = uint8Value + fixtures[23] = &uint8Value + + var intNilPointer *int + var int64NilPointer *int64 + var int32NilPointer *int32 + var int16NilPointer *int16 + var int8NilPointer *int8 + var float64NilPointer *float64 + var float32NilPointer *float32 + var uintNilPointer *uint + var uint64NilPointer *uint64 + var uint32NilPointer *uint32 + var uint16NilPointer *uint16 + var uint8NilPointer *uint8 + + nilPointerFixtures := make([]interface{}, 12) + nilPointerFixtures[0] = intNilPointer + nilPointerFixtures[1] = int64NilPointer + nilPointerFixtures[2] = int32NilPointer + nilPointerFixtures[3] = int16NilPointer + nilPointerFixtures[4] = int8NilPointer + nilPointerFixtures[5] = float64NilPointer + nilPointerFixtures[6] = float32NilPointer + nilPointerFixtures[7] = uintNilPointer + nilPointerFixtures[8] = uint64NilPointer + nilPointerFixtures[9] = uint32NilPointer + nilPointerFixtures[10] = uint16NilPointer + nilPointerFixtures[11] = uint8NilPointer + + Convey("When converting values to float should return expected value", func() { + for _, f := range fixtures { + value, _ := ConvertSqlValueColumnToFloat("col", f) + + if !value.Valid { + t.Fatalf("Failed to convert %T value, expected a valid float value", f) + } + + if value.Float64 != null.FloatFrom(1).Float64 { + t.Fatalf("Failed to convert %T value, expected a float value of 1.000, but got %v", f, value) + } + } + }) + + Convey("When converting nil pointer values to float should return expected value", func() { + for _, f := range nilPointerFixtures { + value, err := ConvertSqlValueColumnToFloat("col", f) + + if err != nil { + t.Fatalf("Failed to convert %T value, expected a non nil error, but got %v", f, err) + } + + if value.Valid { + t.Fatalf("Failed to convert %T value, expected an invalid float value", f) + } + } + }) + }) }) }