2017-12-04 02:56:21 +08:00
package mssql
import (
2018-03-15 16:51:29 +08:00
"fmt"
2018-03-16 21:42:54 +08:00
"math/rand"
2017-12-06 15:32:20 +08:00
"strings"
2017-12-04 02:56:21 +08:00
"testing"
"time"
"github.com/go-xorm/xorm"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/services/sqlstore/sqlutil"
"github.com/grafana/grafana/pkg/tsdb"
. "github.com/smartystreets/goconvey/convey"
)
// To run this test, remove the Skip from SkipConvey
2018-03-16 21:42:54 +08:00
// and set up a MSSQL db named grafanatest and a user/password grafana/Password!
// Use the docker/blocks/mssql_tests/docker-compose.yaml to spin up a
// preconfigured MSSQL server suitable for running these tests.
// If needed, change the variable below to the IP address of the database.
2018-03-15 16:51:29 +08:00
var serverIP string = "localhost"
2017-12-06 15:32:20 +08:00
2017-12-04 02:56:21 +08:00
func TestMSSQL ( t * testing . T ) {
2018-03-15 22:27:31 +08:00
SkipConvey ( "MSSQL" , t , func ( ) {
2017-12-04 02:56:21 +08:00
x := InitMSSQLTestDB ( t )
endpoint := & MssqlQueryEndpoint {
sqlEngine : & tsdb . DefaultSqlEngine {
MacroEngine : NewMssqlMacroEngine ( ) ,
XormEngine : x ,
} ,
log : log . New ( "tsdb.mssql" ) ,
}
sess := x . NewSession ( )
defer sess . Close ( )
2018-03-16 21:37:16 +08:00
fromStart := time . Date ( 2018 , 3 , 15 , 13 , 0 , 0 , 0 , time . UTC )
2018-03-15 21:26:53 +08:00
Convey ( "Given a table with different native data types" , func ( ) {
sql := `
2018-03-20 21:38:56 +08:00
IF OBJECT_ID ( ' dbo . [ mssql_types ] ' , 'U' ) IS NOT NULL
DROP TABLE dbo . [ mssql_types ]
CREATE TABLE [ mssql_types ] (
c_bit bit ,
c_tinyint tinyint ,
c_smallint smallint ,
c_int int ,
c_bigint bigint ,
c_money money ,
c_smallmoney smallmoney ,
c_numeric numeric ( 10 , 5 ) ,
c_real real ,
c_decimal decimal ( 10 , 2 ) ,
c_float float ,
c_char char ( 10 ) ,
c_varchar varchar ( 10 ) ,
c_text text ,
c_nchar nchar ( 12 ) ,
c_nvarchar nvarchar ( 12 ) ,
c_ntext ntext ,
c_datetime datetime ,
c_datetime2 datetime2 ,
c_smalldatetime smalldatetime ,
c_date date ,
c_time time ,
c_datetimeoffset datetimeoffset
)
`
2018-03-15 21:26:53 +08:00
_ , err := sess . Exec ( sql )
So ( err , ShouldBeNil )
dt := time . Date ( 2018 , 3 , 14 , 21 , 20 , 6 , 527e6 , time . UTC )
dtFormat := "2006-01-02 15:04:05.999999999"
d := dt . Format ( dtFormat )
dt2 := time . Date ( 2018 , 3 , 14 , 21 , 20 , 6 , 8896406e2 , time . UTC )
dt2Format := "2006-01-02 15:04:05.999999999 -07:00"
d2 := dt2 . Format ( dt2Format )
sql = fmt . Sprintf ( `
2018-03-20 21:38:56 +08:00
INSERT INTO [ mssql_types ]
SELECT
1 , 5 , 20020 , 980300 , 1420070400 , ' $ 20000.15 ' , ' £ 2.15 ' , 12345.12 ,
1.11 , 2.22 , 3.33 ,
' char10 ' , ' varchar10 ' , ' text ' ,
N ' ☺ nchar12 ☺ ' , N ' ☺ nvarchar12 ☺ ' , N ' ☺ text ☺ ' ,
CAST ( ' % s ' AS DATETIME ) , CAST ( ' % s ' AS DATETIME2 ) , CAST ( ' % s ' AS SMALLDATETIME ) , CAST ( ' % s ' AS DATE ) , CAST ( ' % s ' AS TIME ) , SWITCHOFFSET ( CAST ( ' % s ' AS DATETIMEOFFSET ) , ' - 07 : 00 ' )
` , d , d2 , d , d , d , d2 )
2018-03-14 02:54:29 +08:00
2018-03-15 21:26:53 +08:00
_ , err = sess . Exec ( sql )
2017-12-04 02:56:21 +08:00
So ( err , ShouldBeNil )
2018-03-15 22:27:31 +08:00
Convey ( "When doing a table query should map MSSQL column types to Go types" , func ( ) {
2018-03-15 21:26:53 +08:00
query := & tsdb . TsdbQuery {
Queries : [ ] * tsdb . Query {
{
Model : simplejson . NewFromAny ( map [ string ] interface { } {
"rawSql" : "SELECT * FROM mssql_types" ,
"format" : "table" ,
} ) ,
RefId : "A" ,
} ,
} ,
}
resp , err := endpoint . Query ( nil , nil , query )
queryResult := resp . Results [ "A" ]
So ( err , ShouldBeNil )
column := queryResult . Tables [ 0 ] . Rows [ 0 ]
So ( column [ 0 ] . ( bool ) , ShouldEqual , true )
2018-03-16 21:37:16 +08:00
2018-03-15 21:26:53 +08:00
So ( column [ 1 ] . ( int64 ) , ShouldEqual , 5 )
So ( column [ 2 ] . ( int64 ) , ShouldEqual , 20020 )
So ( column [ 3 ] . ( int64 ) , ShouldEqual , 980300 )
So ( column [ 4 ] . ( int64 ) , ShouldEqual , 1420070400 )
2018-03-16 21:37:16 +08:00
So ( column [ 5 ] . ( float64 ) , ShouldEqual , 20000.15 )
So ( column [ 6 ] . ( float64 ) , ShouldEqual , 2.15 )
So ( column [ 7 ] . ( float64 ) , ShouldEqual , 12345.12 )
So ( column [ 8 ] . ( float64 ) , ShouldEqual , 1.1100000143051147 )
So ( column [ 9 ] . ( float64 ) , ShouldEqual , 2.22 )
2018-03-15 21:26:53 +08:00
So ( column [ 10 ] . ( float64 ) , ShouldEqual , 3.33 )
So ( column [ 11 ] . ( string ) , ShouldEqual , "char10 " )
So ( column [ 12 ] . ( string ) , ShouldEqual , "varchar10" )
So ( column [ 13 ] . ( string ) , ShouldEqual , "text" )
So ( column [ 14 ] . ( string ) , ShouldEqual , "☺nchar12☺ " )
So ( column [ 15 ] . ( string ) , ShouldEqual , "☺nvarchar12☺" )
So ( column [ 16 ] . ( string ) , ShouldEqual , "☺text☺" )
So ( column [ 17 ] . ( time . Time ) , ShouldEqual , dt )
So ( column [ 18 ] . ( time . Time ) , ShouldEqual , dt2 )
So ( column [ 19 ] . ( time . Time ) , ShouldEqual , dt . Truncate ( time . Minute ) )
So ( column [ 20 ] . ( time . Time ) , ShouldEqual , dt . Truncate ( 24 * time . Hour ) )
So ( column [ 21 ] . ( time . Time ) , ShouldEqual , time . Date ( 1 , 1 , 1 , dt . Hour ( ) , dt . Minute ( ) , dt . Second ( ) , dt . Nanosecond ( ) , time . UTC ) )
So ( column [ 22 ] . ( time . Time ) , ShouldEqual , dt2 . In ( time . FixedZone ( "UTC" , int ( - 7 * time . Hour ) ) ) )
} )
2018-03-15 16:51:29 +08:00
} )
2018-03-16 21:42:54 +08:00
Convey ( "Given a table with metrics that lacks data for some series " , func ( ) {
2018-03-15 16:51:29 +08:00
sql := `
2018-03-20 21:38:56 +08:00
IF OBJECT_ID ( ' dbo . [ metric ] ' , 'U' ) IS NOT NULL
DROP TABLE dbo . [ metric ]
2018-03-15 21:26:53 +08:00
2018-03-20 21:38:56 +08:00
CREATE TABLE [ metric ] (
time datetime ,
value int
)
`
2018-03-15 21:26:53 +08:00
2018-03-15 16:51:29 +08:00
_ , err := sess . Exec ( sql )
So ( err , ShouldBeNil )
2018-03-15 21:26:53 +08:00
type metric struct {
2018-03-16 21:42:54 +08:00
Time time . Time
Value int64
2018-03-15 21:26:53 +08:00
}
series := [ ] * metric { }
firstRange := genTimeRangeByInterval ( fromStart , 10 * time . Minute , 10 * time . Second )
secondRange := genTimeRangeByInterval ( fromStart . Add ( 20 * time . Minute ) , 10 * time . Minute , 10 * time . Second )
for _ , t := range firstRange {
series = append ( series , & metric {
2018-03-16 21:42:54 +08:00
Time : t ,
Value : 15 ,
2018-03-15 21:26:53 +08:00
} )
}
for _ , t := range secondRange {
series = append ( series , & metric {
2018-03-16 21:42:54 +08:00
Time : t ,
Value : 20 ,
2018-03-15 21:26:53 +08:00
} )
}
dtFormat := "2006-01-02 15:04:05.999999999"
for _ , s := range series {
sql = fmt . Sprintf ( `
2018-03-20 21:38:56 +08:00
INSERT INTO metric ( time , value )
VALUES ( CAST ( ' % s ' AS DATETIME ) , % d )
` , s . Time . Format ( dtFormat ) , s . Value )
2018-03-15 21:26:53 +08:00
_ , err = sess . Exec ( sql )
So ( err , ShouldBeNil )
}
Convey ( "When doing a metric query using timeGroup" , func ( ) {
query := & tsdb . TsdbQuery {
Queries : [ ] * tsdb . Query {
{
Model : simplejson . NewFromAny ( map [ string ] interface { } {
2018-03-16 21:42:54 +08:00
"rawSql" : "SELECT $__timeGroup(time, '5m') AS time, avg(value) as value FROM metric GROUP BY $__timeGroup(time, '5m') ORDER BY 1" ,
2018-03-15 21:26:53 +08:00
"format" : "time_series" ,
} ) ,
RefId : "A" ,
} ,
} ,
}
resp , err := endpoint . Query ( nil , nil , query )
So ( err , ShouldBeNil )
2018-03-22 21:49:40 +08:00
queryResult := resp . Results [ "A" ]
2018-03-15 21:26:53 +08:00
So ( queryResult . Error , ShouldBeNil )
points := queryResult . Series [ 0 ] . Points
2018-03-22 21:49:40 +08:00
So ( len ( points ) , ShouldEqual , 6 )
2018-03-15 21:26:53 +08:00
2018-03-22 21:49:40 +08:00
dt := fromStart
for i := 0 ; i < 3 ; i ++ {
aValue := points [ i ] [ 0 ] . Float64
aTime := time . Unix ( int64 ( points [ i ] [ 1 ] . Float64 ) / 1000 , 0 )
So ( aValue , ShouldEqual , 15 )
So ( aTime , ShouldEqual , dt )
dt = dt . Add ( 5 * time . Minute )
}
2018-03-15 21:26:53 +08:00
2018-03-22 21:49:40 +08:00
// adjust for 5 minute gap
dt = dt . Add ( 5 * time . Minute )
for i := 3 ; i < 6 ; i ++ {
aValue := points [ i ] [ 0 ] . Float64
aTime := time . Unix ( int64 ( points [ i ] [ 1 ] . Float64 ) / 1000 , 0 )
So ( aValue , ShouldEqual , 20 )
So ( aTime , ShouldEqual , dt )
dt = dt . Add ( 5 * time . Minute )
}
2018-03-15 21:26:53 +08:00
} )
2018-03-15 22:06:54 +08:00
Convey ( "When doing a metric query using timeGroup with NULL fill enabled" , func ( ) {
query := & tsdb . TsdbQuery {
Queries : [ ] * tsdb . Query {
{
Model : simplejson . NewFromAny ( map [ string ] interface { } {
2018-03-16 21:42:54 +08:00
"rawSql" : "SELECT $__timeGroup(time, '5m', NULL) AS time, avg(value) as value FROM metric GROUP BY $__timeGroup(time, '5m') ORDER BY 1" ,
2018-03-15 22:06:54 +08:00
"format" : "time_series" ,
} ) ,
RefId : "A" ,
} ,
} ,
TimeRange : & tsdb . TimeRange {
From : fmt . Sprintf ( "%v" , fromStart . Unix ( ) * 1000 ) ,
To : fmt . Sprintf ( "%v" , fromStart . Add ( 34 * time . Minute ) . Unix ( ) * 1000 ) ,
} ,
}
resp , err := endpoint . Query ( nil , nil , query )
So ( err , ShouldBeNil )
2018-03-22 21:49:40 +08:00
queryResult := resp . Results [ "A" ]
2018-03-15 22:06:54 +08:00
So ( queryResult . Error , ShouldBeNil )
points := queryResult . Series [ 0 ] . Points
So ( len ( points ) , ShouldEqual , 7 )
2018-03-22 21:49:40 +08:00
dt := fromStart
for i := 0 ; i < 3 ; i ++ {
aValue := points [ i ] [ 0 ] . Float64
aTime := time . Unix ( int64 ( points [ i ] [ 1 ] . Float64 ) / 1000 , 0 )
So ( aValue , ShouldEqual , 15 )
So ( aTime , ShouldEqual , dt )
dt = dt . Add ( 5 * time . Minute )
}
So ( points [ 3 ] [ 0 ] . Valid , ShouldBeFalse )
// adjust for 5 minute gap
dt = dt . Add ( 5 * time . Minute )
for i := 4 ; i < 7 ; i ++ {
aValue := points [ i ] [ 0 ] . Float64
aTime := time . Unix ( int64 ( points [ i ] [ 1 ] . Float64 ) / 1000 , 0 )
So ( aValue , ShouldEqual , 20 )
So ( aTime , ShouldEqual , dt )
dt = dt . Add ( 5 * time . Minute )
}
2018-03-15 22:06:54 +08:00
} )
Convey ( "When doing a metric query using timeGroup with float fill enabled" , func ( ) {
query := & tsdb . TsdbQuery {
Queries : [ ] * tsdb . Query {
{
Model : simplejson . NewFromAny ( map [ string ] interface { } {
2018-03-16 21:42:54 +08:00
"rawSql" : "SELECT $__timeGroup(time, '5m', 1.5) AS time, avg(value) as value FROM metric GROUP BY $__timeGroup(time, '5m') ORDER BY 1" ,
2018-03-15 22:06:54 +08:00
"format" : "time_series" ,
} ) ,
RefId : "A" ,
} ,
} ,
TimeRange : & tsdb . TimeRange {
From : fmt . Sprintf ( "%v" , fromStart . Unix ( ) * 1000 ) ,
To : fmt . Sprintf ( "%v" , fromStart . Add ( 34 * time . Minute ) . Unix ( ) * 1000 ) ,
} ,
}
resp , err := endpoint . Query ( nil , nil , query )
So ( err , ShouldBeNil )
2018-03-22 21:49:40 +08:00
queryResult := resp . Results [ "A" ]
2018-03-15 22:06:54 +08:00
So ( queryResult . Error , ShouldBeNil )
points := queryResult . Series [ 0 ] . Points
2018-03-22 21:49:40 +08:00
So ( points [ 3 ] [ 0 ] . Float64 , ShouldEqual , 1.5 )
2018-03-15 22:06:54 +08:00
} )
2017-12-04 02:56:21 +08:00
} )
2018-03-16 21:42:54 +08:00
Convey ( "Given a table with metrics having multiple values and measurements" , func ( ) {
sql := `
2018-03-20 21:38:56 +08:00
IF OBJECT_ID ( ' dbo . [ metric_values ] ' , 'U' ) IS NOT NULL
DROP TABLE dbo . [ metric_values ]
CREATE TABLE [ metric_values ] (
time datetime ,
measurement nvarchar ( 100 ) ,
valueOne int ,
valueTwo int ,
)
`
2018-03-16 21:42:54 +08:00
_ , err := sess . Exec ( sql )
So ( err , ShouldBeNil )
type metricValues struct {
Time time . Time
Measurement string
ValueOne int64
ValueTwo int64
}
rand . Seed ( time . Now ( ) . Unix ( ) )
rnd := func ( min , max int64 ) int64 {
return rand . Int63n ( max - min ) + min
}
series := [ ] * metricValues { }
for _ , t := range genTimeRangeByInterval ( fromStart . Add ( - 30 * time . Minute ) , 90 * time . Minute , 5 * time . Minute ) {
series = append ( series , & metricValues {
Time : t ,
Measurement : "Metric A" ,
ValueOne : rnd ( 0 , 100 ) ,
ValueTwo : rnd ( 0 , 100 ) ,
} )
series = append ( series , & metricValues {
Time : t ,
Measurement : "Metric B" ,
ValueOne : rnd ( 0 , 100 ) ,
ValueTwo : rnd ( 0 , 100 ) ,
} )
}
dtFormat := "2006-01-02 15:04:05"
for _ , s := range series {
sql = fmt . Sprintf ( `
2018-03-20 21:38:56 +08:00
INSERT metric_values ( time , measurement , valueOne , valueTwo )
VALUES ( CAST ( ' % s ' AS DATETIME ) , ' % s ' , % d , % d )
` , s . Time . Format ( dtFormat ) , s . Measurement , s . ValueOne , s . ValueTwo )
2018-03-16 21:42:54 +08:00
_ , err = sess . Exec ( sql )
So ( err , ShouldBeNil )
}
Convey ( "When doing a metric query grouping by time and select metric column should return correct series" , func ( ) {
query := & tsdb . TsdbQuery {
Queries : [ ] * tsdb . Query {
{
Model : simplejson . NewFromAny ( map [ string ] interface { } {
2018-03-20 00:14:01 +08:00
"rawSql" : "SELECT $__timeEpoch(time), measurement + ' - value one' as metric, valueOne FROM metric_values ORDER BY 1" ,
2018-03-16 21:42:54 +08:00
"format" : "time_series" ,
} ) ,
RefId : "A" ,
} ,
} ,
}
resp , err := endpoint . Query ( nil , nil , query )
queryResult := resp . Results [ "A" ]
So ( err , ShouldBeNil )
So ( queryResult . Error , ShouldBeNil )
2018-03-20 00:14:01 +08:00
So ( len ( queryResult . Series ) , ShouldEqual , 2 )
So ( queryResult . Series [ 0 ] . Name , ShouldEqual , "Metric A - value one" )
So ( queryResult . Series [ 1 ] . Name , ShouldEqual , "Metric B - value one" )
2018-03-16 21:42:54 +08:00
} )
Convey ( "When doing a metric query grouping by time should return correct series" , func ( ) {
query := & tsdb . TsdbQuery {
Queries : [ ] * tsdb . Query {
{
Model : simplejson . NewFromAny ( map [ string ] interface { } {
"rawSql" : "SELECT $__timeEpoch(time), valueOne, valueTwo FROM metric_values ORDER BY 1" ,
"format" : "time_series" ,
} ) ,
RefId : "A" ,
} ,
} ,
}
resp , err := endpoint . Query ( nil , nil , query )
queryResult := resp . Results [ "A" ]
So ( err , ShouldBeNil )
So ( queryResult . Error , ShouldBeNil )
So ( len ( queryResult . Series ) , ShouldEqual , 2 )
So ( queryResult . Series [ 0 ] . Name , ShouldEqual , "valueOne" )
So ( queryResult . Series [ 1 ] . Name , ShouldEqual , "valueTwo" )
} )
2018-03-20 02:56:34 +08:00
Convey ( "Given a stored procedure that takes @from and @to in epoch time" , func ( ) {
sql := `
2018-03-20 21:38:56 +08:00
IF object_id ( ' sp_test_epoch ' ) IS NOT NULL
DROP PROCEDURE sp_test_epoch
`
2018-03-20 02:56:34 +08:00
_ , err := sess . Exec ( sql )
So ( err , ShouldBeNil )
sql = `
2018-03-20 21:38:56 +08:00
CREATE PROCEDURE sp_test_epoch (
@ from int ,
@ to int
) AS
BEGIN
SELECT
cast ( cast ( DATEDIFF ( second , { d ' 1970 - 01 - 01 ' } , DATEADD ( second , DATEDIFF ( second , GETDATE ( ) , GETUTCDATE ( ) ) , time ) ) / 600 as int ) * 600 as int ) as time ,
measurement + ' - value one ' as metric ,
avg ( valueOne ) as value
FROM
metric_values
WHERE
time >= DATEADD ( s , @ from , ' 1970 - 01 - 01 ' ) AND time <= DATEADD ( s , @ to , ' 1970 - 01 - 01 ' )
GROUP BY
cast ( cast ( DATEDIFF ( second , { d ' 1970 - 01 - 01 ' } , DATEADD ( second , DATEDIFF ( second , GETDATE ( ) , GETUTCDATE ( ) ) , time ) ) / 600 as int ) * 600 as int ) ,
measurement
UNION ALL
SELECT
cast ( cast ( DATEDIFF ( second , { d ' 1970 - 01 - 01 ' } , DATEADD ( second , DATEDIFF ( second , GETDATE ( ) , GETUTCDATE ( ) ) , time ) ) / 600 as int ) * 600 as int ) as time ,
measurement + ' - value two ' as metric ,
avg ( valueTwo ) as value
FROM
metric_values
WHERE
time >= DATEADD ( s , @ from , ' 1970 - 01 - 01 ' ) AND time <= DATEADD ( s , @ to , ' 1970 - 01 - 01 ' )
GROUP BY
cast ( cast ( DATEDIFF ( second , { d ' 1970 - 01 - 01 ' } , DATEADD ( second , DATEDIFF ( second , GETDATE ( ) , GETUTCDATE ( ) ) , time ) ) / 600 as int ) * 600 as int ) ,
measurement
ORDER BY 1
END
`
2018-03-20 02:56:34 +08:00
_ , err = sess . Exec ( sql )
So ( err , ShouldBeNil )
Convey ( "When doing a metric query using stored procedure should return correct result" , func ( ) {
query := & tsdb . TsdbQuery {
Queries : [ ] * tsdb . Query {
{
Model : simplejson . NewFromAny ( map [ string ] interface { } {
"rawSql" : ` DECLARE
2018-03-20 21:38:56 +08:00
@ from int = $ __unixEpochFrom ( ) ,
@ to int = $ __unixEpochTo ( )
2018-03-20 02:56:34 +08:00
2018-03-20 21:38:56 +08:00
EXEC dbo . sp_test_epoch @ from , @ to ` ,
2018-03-20 02:56:34 +08:00
"format" : "time_series" ,
} ) ,
RefId : "A" ,
} ,
} ,
TimeRange : & tsdb . TimeRange {
From : "1521117000000" ,
To : "1521122100000" ,
} ,
}
resp , err := endpoint . Query ( nil , nil , query )
queryResult := resp . Results [ "A" ]
So ( err , ShouldBeNil )
So ( queryResult . Error , ShouldBeNil )
So ( len ( queryResult . Series ) , ShouldEqual , 4 )
So ( queryResult . Series [ 0 ] . Name , ShouldEqual , "Metric A - value one" )
So ( queryResult . Series [ 1 ] . Name , ShouldEqual , "Metric B - value one" )
So ( queryResult . Series [ 2 ] . Name , ShouldEqual , "Metric A - value two" )
So ( queryResult . Series [ 3 ] . Name , ShouldEqual , "Metric B - value two" )
} )
} )
Convey ( "Given a stored procedure that takes @from and @to in datetime" , func ( ) {
sql := `
2018-03-20 21:38:56 +08:00
IF object_id ( ' sp_test_datetime ' ) IS NOT NULL
DROP PROCEDURE sp_test_datetime
`
2018-03-20 02:56:34 +08:00
_ , err := sess . Exec ( sql )
So ( err , ShouldBeNil )
sql = `
2018-03-20 21:38:56 +08:00
CREATE PROCEDURE sp_test_datetime (
@ from datetime ,
@ to datetime
) AS
BEGIN
SELECT
cast ( cast ( DATEDIFF ( second , { d ' 1970 - 01 - 01 ' } , time ) / 600 as int ) * 600 as int ) as time ,
measurement + ' - value one ' as metric ,
avg ( valueOne ) as value
FROM
metric_values
WHERE
time >= @ from AND time <= @ to
GROUP BY
cast ( cast ( DATEDIFF ( second , { d ' 1970 - 01 - 01 ' } , time ) / 600 as int ) * 600 as int ) ,
measurement
UNION ALL
SELECT
cast ( cast ( DATEDIFF ( second , { d ' 1970 - 01 - 01 ' } , time ) / 600 as int ) * 600 as int ) as time ,
measurement + ' - value two ' as metric ,
avg ( valueTwo ) as value
FROM
metric_values
WHERE
time >= @ from AND time <= @ to
GROUP BY
cast ( cast ( DATEDIFF ( second , { d ' 1970 - 01 - 01 ' } , time ) / 600 as int ) * 600 as int ) ,
measurement
ORDER BY 1
END
`
2018-03-20 02:56:34 +08:00
_ , err = sess . Exec ( sql )
So ( err , ShouldBeNil )
Convey ( "When doing a metric query using stored procedure should return correct result" , func ( ) {
query := & tsdb . TsdbQuery {
Queries : [ ] * tsdb . Query {
{
Model : simplejson . NewFromAny ( map [ string ] interface { } {
"rawSql" : ` DECLARE
2018-03-20 21:38:56 +08:00
@ from int = $ __unixEpochFrom ( ) ,
@ to int = $ __unixEpochTo ( )
2018-03-20 02:56:34 +08:00
2018-03-20 21:38:56 +08:00
EXEC dbo . sp_test_epoch @ from , @ to ` ,
2018-03-20 02:56:34 +08:00
"format" : "time_series" ,
} ) ,
RefId : "A" ,
} ,
} ,
TimeRange : & tsdb . TimeRange {
From : "1521117000000" ,
To : "1521122100000" ,
} ,
}
resp , err := endpoint . Query ( nil , nil , query )
queryResult := resp . Results [ "A" ]
So ( err , ShouldBeNil )
So ( queryResult . Error , ShouldBeNil )
So ( len ( queryResult . Series ) , ShouldEqual , 4 )
So ( queryResult . Series [ 0 ] . Name , ShouldEqual , "Metric A - value one" )
So ( queryResult . Series [ 1 ] . Name , ShouldEqual , "Metric B - value one" )
So ( queryResult . Series [ 2 ] . Name , ShouldEqual , "Metric A - value two" )
So ( queryResult . Series [ 3 ] . Name , ShouldEqual , "Metric B - value two" )
} )
} )
2018-03-16 21:42:54 +08:00
} )
Convey ( "Given a table with event data" , func ( ) {
sql := `
IF OBJECT_ID ( ' dbo . [ event ] ' , 'U' ) IS NOT NULL
DROP TABLE dbo . [ event ]
CREATE TABLE [ event ] (
time_sec bigint ,
description nvarchar ( 100 ) ,
tags nvarchar ( 100 ) ,
)
`
_ , err := sess . Exec ( sql )
So ( err , ShouldBeNil )
type event struct {
TimeSec int64
Description string
Tags string
}
events := [ ] * event { }
for _ , t := range genTimeRangeByInterval ( fromStart . Add ( - 20 * time . Minute ) , 60 * time . Minute , 25 * time . Minute ) {
events = append ( events , & event {
TimeSec : t . Unix ( ) ,
Description : "Someone deployed something" ,
Tags : "deploy" ,
} )
events = append ( events , & event {
TimeSec : t . Add ( 5 * time . Minute ) . Unix ( ) ,
Description : "New support ticket registered" ,
Tags : "ticket" ,
} )
}
for _ , e := range events {
sql = fmt . Sprintf ( `
INSERT [ event ] ( time_sec , description , tags )
VALUES ( % d , ' % s ' , ' % s ' )
` , e . TimeSec , e . Description , e . Tags )
_ , err = sess . Exec ( sql )
So ( err , ShouldBeNil )
}
Convey ( "When doing an annotation query of deploy events should return expected result" , func ( ) {
query := & tsdb . TsdbQuery {
Queries : [ ] * tsdb . Query {
{
Model : simplejson . NewFromAny ( map [ string ] interface { } {
"rawSql" : "SELECT time_sec as time, description as [text], tags FROM [event] WHERE $__unixEpochFilter(time_sec) AND tags='deploy' ORDER BY 1 ASC" ,
"format" : "table" ,
} ) ,
RefId : "Deploys" ,
} ,
} ,
TimeRange : & tsdb . TimeRange {
From : fmt . Sprintf ( "%v" , fromStart . Add ( - 20 * time . Minute ) . Unix ( ) * 1000 ) ,
To : fmt . Sprintf ( "%v" , fromStart . Add ( 40 * time . Minute ) . Unix ( ) * 1000 ) ,
} ,
}
resp , err := endpoint . Query ( nil , nil , query )
queryResult := resp . Results [ "Deploys" ]
So ( err , ShouldBeNil )
So ( len ( queryResult . Tables [ 0 ] . Rows ) , ShouldEqual , 3 )
} )
Convey ( "When doing an annotation query of ticket events should return expected result" , func ( ) {
query := & tsdb . TsdbQuery {
Queries : [ ] * tsdb . Query {
{
Model : simplejson . NewFromAny ( map [ string ] interface { } {
"rawSql" : "SELECT time_sec as time, description as [text], tags FROM [event] WHERE $__unixEpochFilter(time_sec) AND tags='ticket' ORDER BY 1 ASC" ,
"format" : "table" ,
} ) ,
RefId : "Tickets" ,
} ,
} ,
TimeRange : & tsdb . TimeRange {
From : fmt . Sprintf ( "%v" , fromStart . Add ( - 20 * time . Minute ) . Unix ( ) * 1000 ) ,
To : fmt . Sprintf ( "%v" , fromStart . Add ( 40 * time . Minute ) . Unix ( ) * 1000 ) ,
} ,
}
resp , err := endpoint . Query ( nil , nil , query )
queryResult := resp . Results [ "Tickets" ]
So ( err , ShouldBeNil )
So ( len ( queryResult . Tables [ 0 ] . Rows ) , ShouldEqual , 3 )
} )
2018-03-20 21:38:56 +08:00
Convey ( "When doing an annotation query with a time column in datetime format" , func ( ) {
query := & tsdb . TsdbQuery {
Queries : [ ] * tsdb . Query {
{
Model : simplejson . NewFromAny ( map [ string ] interface { } {
"rawSql" : "SELECT DATEADD(s, time_sec, {d '1970-01-01'}) AS time, description as [text], tags FROM [event] WHERE $__unixEpochFilter(time_sec) AND tags='ticket' ORDER BY 1 ASC" ,
"format" : "table" ,
} ) ,
RefId : "Tickets" ,
} ,
} ,
TimeRange : & tsdb . TimeRange {
From : fmt . Sprintf ( "%v" , fromStart . Add ( - 20 * time . Minute ) . Unix ( ) * 1000 ) ,
To : fmt . Sprintf ( "%v" , fromStart . Add ( 40 * time . Minute ) . Unix ( ) * 1000 ) ,
} ,
}
resp , err := endpoint . Query ( nil , nil , query )
queryResult := resp . Results [ "Tickets" ]
So ( err , ShouldBeNil )
So ( len ( queryResult . Tables [ 0 ] . Rows ) , ShouldEqual , 3 )
columns := queryResult . Tables [ 0 ] . Rows [ 0 ]
//Should be in milliseconds
So ( columns [ 0 ] . ( float64 ) , ShouldBeGreaterThan , 1000000000000 )
} )
2018-03-16 21:42:54 +08:00
} )
2017-12-04 02:56:21 +08:00
} )
}
func InitMSSQLTestDB ( t * testing . T ) * xorm . Engine {
2017-12-06 15:32:20 +08:00
x , err := xorm . NewEngine ( sqlutil . TestDB_Mssql . DriverName , strings . Replace ( sqlutil . TestDB_Mssql . ConnStr , "localhost" , serverIP , 1 ) )
2017-12-04 02:56:21 +08:00
// x.ShowSQL()
if err != nil {
t . Fatalf ( "Failed to init mssql db %v" , err )
}
sqlutil . CleanDB ( x )
return x
}
2018-03-15 16:51:29 +08:00
func genTimeRangeByInterval ( from time . Time , duration time . Duration , interval time . Duration ) [ ] time . Time {
durationSec := int64 ( duration . Seconds ( ) )
intervalSec := int64 ( interval . Seconds ( ) )
timeRange := [ ] time . Time { }
2018-03-15 21:26:53 +08:00
for i := int64 ( 0 ) ; i < durationSec ; i += intervalSec {
2018-03-15 16:51:29 +08:00
timeRange = append ( timeRange , from )
from = from . Add ( time . Duration ( int64 ( time . Second ) * intervalSec ) )
}
return timeRange
}