mssql_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. package mssql
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "strings"
  6. "testing"
  7. "time"
  8. "github.com/go-xorm/xorm"
  9. "github.com/grafana/grafana/pkg/components/simplejson"
  10. "github.com/grafana/grafana/pkg/log"
  11. "github.com/grafana/grafana/pkg/services/sqlstore/sqlutil"
  12. "github.com/grafana/grafana/pkg/tsdb"
  13. . "github.com/smartystreets/goconvey/convey"
  14. )
  15. // To run this test, remove the Skip from SkipConvey
  16. // and set up a MSSQL db named grafanatest and a user/password grafana/Password!
  17. // Use the docker/blocks/mssql_tests/docker-compose.yaml to spin up a
  18. // preconfigured MSSQL server suitable for running these tests.
  19. // If needed, change the variable below to the IP address of the database.
  20. var serverIP string = "localhost"
  21. func TestMSSQL(t *testing.T) {
  22. SkipConvey("MSSQL", t, func() {
  23. x := InitMSSQLTestDB(t)
  24. endpoint := &MssqlQueryEndpoint{
  25. sqlEngine: &tsdb.DefaultSqlEngine{
  26. MacroEngine: NewMssqlMacroEngine(),
  27. XormEngine: x,
  28. },
  29. log: log.New("tsdb.mssql"),
  30. }
  31. sess := x.NewSession()
  32. defer sess.Close()
  33. fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC)
  34. Convey("Given a table with different native data types", func() {
  35. sql := `
  36. IF OBJECT_ID('dbo.[mssql_types]', 'U') IS NOT NULL
  37. DROP TABLE dbo.[mssql_types]
  38. CREATE TABLE [mssql_types] (
  39. c_bit bit,
  40. c_tinyint tinyint,
  41. c_smallint smallint,
  42. c_int int,
  43. c_bigint bigint,
  44. c_money money,
  45. c_smallmoney smallmoney,
  46. c_numeric numeric(10,5),
  47. c_real real,
  48. c_decimal decimal(10,2),
  49. c_float float,
  50. c_char char(10),
  51. c_varchar varchar(10),
  52. c_text text,
  53. c_nchar nchar(12),
  54. c_nvarchar nvarchar(12),
  55. c_ntext ntext,
  56. c_datetime datetime,
  57. c_datetime2 datetime2,
  58. c_smalldatetime smalldatetime,
  59. c_date date,
  60. c_time time,
  61. c_datetimeoffset datetimeoffset
  62. )
  63. `
  64. _, err := sess.Exec(sql)
  65. So(err, ShouldBeNil)
  66. dt := time.Date(2018, 3, 14, 21, 20, 6, 527e6, time.UTC)
  67. dtFormat := "2006-01-02 15:04:05.999999999"
  68. d := dt.Format(dtFormat)
  69. dt2 := time.Date(2018, 3, 14, 21, 20, 6, 8896406e2, time.UTC)
  70. dt2Format := "2006-01-02 15:04:05.999999999 -07:00"
  71. d2 := dt2.Format(dt2Format)
  72. sql = fmt.Sprintf(`
  73. INSERT INTO [mssql_types]
  74. SELECT
  75. 1, 5, 20020, 980300, 1420070400, '$20000.15', '£2.15', 12345.12,
  76. 1.11, 2.22, 3.33,
  77. 'char10', 'varchar10', 'text',
  78. N'☺nchar12☺', N'☺nvarchar12☺', N'☺text☺',
  79. 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')
  80. `, d, d2, d, d, d, d2)
  81. _, err = sess.Exec(sql)
  82. So(err, ShouldBeNil)
  83. Convey("When doing a table query should map MSSQL column types to Go types", func() {
  84. query := &tsdb.TsdbQuery{
  85. Queries: []*tsdb.Query{
  86. {
  87. Model: simplejson.NewFromAny(map[string]interface{}{
  88. "rawSql": "SELECT * FROM mssql_types",
  89. "format": "table",
  90. }),
  91. RefId: "A",
  92. },
  93. },
  94. }
  95. resp, err := endpoint.Query(nil, nil, query)
  96. queryResult := resp.Results["A"]
  97. So(err, ShouldBeNil)
  98. column := queryResult.Tables[0].Rows[0]
  99. So(column[0].(bool), ShouldEqual, true)
  100. So(column[1].(int64), ShouldEqual, 5)
  101. So(column[2].(int64), ShouldEqual, 20020)
  102. So(column[3].(int64), ShouldEqual, 980300)
  103. So(column[4].(int64), ShouldEqual, 1420070400)
  104. So(column[5].(float64), ShouldEqual, 20000.15)
  105. So(column[6].(float64), ShouldEqual, 2.15)
  106. So(column[7].(float64), ShouldEqual, 12345.12)
  107. So(column[8].(float64), ShouldEqual, 1.1100000143051147)
  108. So(column[9].(float64), ShouldEqual, 2.22)
  109. So(column[10].(float64), ShouldEqual, 3.33)
  110. So(column[11].(string), ShouldEqual, "char10 ")
  111. So(column[12].(string), ShouldEqual, "varchar10")
  112. So(column[13].(string), ShouldEqual, "text")
  113. So(column[14].(string), ShouldEqual, "☺nchar12☺ ")
  114. So(column[15].(string), ShouldEqual, "☺nvarchar12☺")
  115. So(column[16].(string), ShouldEqual, "☺text☺")
  116. So(column[17].(time.Time), ShouldEqual, dt)
  117. So(column[18].(time.Time), ShouldEqual, dt2)
  118. So(column[19].(time.Time), ShouldEqual, dt.Truncate(time.Minute))
  119. So(column[20].(time.Time), ShouldEqual, dt.Truncate(24*time.Hour))
  120. So(column[21].(time.Time), ShouldEqual, time.Date(1, 1, 1, dt.Hour(), dt.Minute(), dt.Second(), dt.Nanosecond(), time.UTC))
  121. So(column[22].(time.Time), ShouldEqual, dt2.In(time.FixedZone("UTC", int(-7*time.Hour))))
  122. })
  123. })
  124. Convey("Given a table with metrics that lacks data for some series ", func() {
  125. sql := `
  126. IF OBJECT_ID('dbo.[metric]', 'U') IS NOT NULL
  127. DROP TABLE dbo.[metric]
  128. CREATE TABLE [metric] (
  129. time datetime,
  130. value int
  131. )
  132. `
  133. _, err := sess.Exec(sql)
  134. So(err, ShouldBeNil)
  135. type metric struct {
  136. Time time.Time
  137. Value int64
  138. }
  139. series := []*metric{}
  140. firstRange := genTimeRangeByInterval(fromStart, 10*time.Minute, 10*time.Second)
  141. secondRange := genTimeRangeByInterval(fromStart.Add(20*time.Minute), 10*time.Minute, 10*time.Second)
  142. for _, t := range firstRange {
  143. series = append(series, &metric{
  144. Time: t,
  145. Value: 15,
  146. })
  147. }
  148. for _, t := range secondRange {
  149. series = append(series, &metric{
  150. Time: t,
  151. Value: 20,
  152. })
  153. }
  154. dtFormat := "2006-01-02 15:04:05.999999999"
  155. for _, s := range series {
  156. sql = fmt.Sprintf(`
  157. INSERT INTO metric (time, value)
  158. VALUES(CAST('%s' AS DATETIME), %d)
  159. `, s.Time.Format(dtFormat), s.Value)
  160. _, err = sess.Exec(sql)
  161. So(err, ShouldBeNil)
  162. }
  163. Convey("When doing a metric query using timeGroup", func() {
  164. query := &tsdb.TsdbQuery{
  165. Queries: []*tsdb.Query{
  166. {
  167. Model: simplejson.NewFromAny(map[string]interface{}{
  168. "rawSql": "SELECT $__timeGroup(time, '5m') AS time, avg(value) as value FROM metric GROUP BY $__timeGroup(time, '5m') ORDER BY 1",
  169. "format": "time_series",
  170. }),
  171. RefId: "A",
  172. },
  173. },
  174. }
  175. resp, err := endpoint.Query(nil, nil, query)
  176. queryResult := resp.Results["A"]
  177. So(err, ShouldBeNil)
  178. So(queryResult.Error, ShouldBeNil)
  179. points := queryResult.Series[0].Points
  180. So(len(points), ShouldEqual, 4)
  181. actualValueFirst := points[0][0].Float64
  182. actualTimeFirst := time.Unix(int64(points[0][1].Float64)/1000, 0)
  183. So(actualValueFirst, ShouldEqual, 15)
  184. So(actualTimeFirst, ShouldEqual, fromStart)
  185. actualValueLast := points[3][0].Float64
  186. actualTimeLast := time.Unix(int64(points[3][1].Float64)/1000, 0)
  187. So(actualValueLast, ShouldEqual, 20)
  188. So(actualTimeLast, ShouldEqual, fromStart.Add(25*time.Minute))
  189. })
  190. Convey("When doing a metric query using timeGroup with NULL fill enabled", func() {
  191. query := &tsdb.TsdbQuery{
  192. Queries: []*tsdb.Query{
  193. {
  194. Model: simplejson.NewFromAny(map[string]interface{}{
  195. "rawSql": "SELECT $__timeGroup(time, '5m', NULL) AS time, avg(value) as value FROM metric GROUP BY $__timeGroup(time, '5m') ORDER BY 1",
  196. "format": "time_series",
  197. }),
  198. RefId: "A",
  199. },
  200. },
  201. TimeRange: &tsdb.TimeRange{
  202. From: fmt.Sprintf("%v", fromStart.Unix()*1000),
  203. To: fmt.Sprintf("%v", fromStart.Add(34*time.Minute).Unix()*1000),
  204. },
  205. }
  206. resp, err := endpoint.Query(nil, nil, query)
  207. queryResult := resp.Results["A"]
  208. So(err, ShouldBeNil)
  209. So(queryResult.Error, ShouldBeNil)
  210. points := queryResult.Series[0].Points
  211. So(len(points), ShouldEqual, 7)
  212. actualValueFirst := points[0][0].Float64
  213. actualTimeFirst := time.Unix(int64(points[0][1].Float64)/1000, 0)
  214. So(actualValueFirst, ShouldEqual, 15)
  215. So(actualTimeFirst, ShouldEqual, fromStart)
  216. actualNullPoint := points[3][0]
  217. actualNullTime := time.Unix(int64(points[3][1].Float64)/1000, 0)
  218. So(actualNullPoint.Valid, ShouldBeFalse)
  219. So(actualNullTime, ShouldEqual, fromStart.Add(15*time.Minute))
  220. actualValueLast := points[5][0].Float64
  221. actualTimeLast := time.Unix(int64(points[5][1].Float64)/1000, 0)
  222. So(actualValueLast, ShouldEqual, 20)
  223. So(actualTimeLast, ShouldEqual, fromStart.Add(25*time.Minute))
  224. actualLastNullPoint := points[6][0]
  225. actualLastNullTime := time.Unix(int64(points[6][1].Float64)/1000, 0)
  226. So(actualLastNullPoint.Valid, ShouldBeFalse)
  227. So(actualLastNullTime, ShouldEqual, fromStart.Add(30*time.Minute))
  228. })
  229. Convey("When doing a metric query using timeGroup with float fill enabled", func() {
  230. query := &tsdb.TsdbQuery{
  231. Queries: []*tsdb.Query{
  232. {
  233. Model: simplejson.NewFromAny(map[string]interface{}{
  234. "rawSql": "SELECT $__timeGroup(time, '5m', 1.5) AS time, avg(value) as value FROM metric GROUP BY $__timeGroup(time, '5m') ORDER BY 1",
  235. "format": "time_series",
  236. }),
  237. RefId: "A",
  238. },
  239. },
  240. TimeRange: &tsdb.TimeRange{
  241. From: fmt.Sprintf("%v", fromStart.Unix()*1000),
  242. To: fmt.Sprintf("%v", fromStart.Add(34*time.Minute).Unix()*1000),
  243. },
  244. }
  245. resp, err := endpoint.Query(nil, nil, query)
  246. queryResult := resp.Results["A"]
  247. So(err, ShouldBeNil)
  248. So(queryResult.Error, ShouldBeNil)
  249. points := queryResult.Series[0].Points
  250. So(points[6][0].Float64, ShouldEqual, 1.5)
  251. })
  252. })
  253. Convey("Given a table with metrics having multiple values and measurements", func() {
  254. sql := `
  255. IF OBJECT_ID('dbo.[metric_values]', 'U') IS NOT NULL
  256. DROP TABLE dbo.[metric_values]
  257. CREATE TABLE [metric_values] (
  258. time datetime,
  259. measurement nvarchar(100),
  260. valueOne int,
  261. valueTwo int,
  262. )
  263. `
  264. _, err := sess.Exec(sql)
  265. So(err, ShouldBeNil)
  266. type metricValues struct {
  267. Time time.Time
  268. Measurement string
  269. ValueOne int64
  270. ValueTwo int64
  271. }
  272. rand.Seed(time.Now().Unix())
  273. rnd := func(min, max int64) int64 {
  274. return rand.Int63n(max-min) + min
  275. }
  276. series := []*metricValues{}
  277. for _, t := range genTimeRangeByInterval(fromStart.Add(-30*time.Minute), 90*time.Minute, 5*time.Minute) {
  278. series = append(series, &metricValues{
  279. Time: t,
  280. Measurement: "Metric A",
  281. ValueOne: rnd(0, 100),
  282. ValueTwo: rnd(0, 100),
  283. })
  284. series = append(series, &metricValues{
  285. Time: t,
  286. Measurement: "Metric B",
  287. ValueOne: rnd(0, 100),
  288. ValueTwo: rnd(0, 100),
  289. })
  290. }
  291. dtFormat := "2006-01-02 15:04:05"
  292. for _, s := range series {
  293. sql = fmt.Sprintf(`
  294. INSERT metric_values (time, measurement, valueOne, valueTwo)
  295. VALUES(CAST('%s' AS DATETIME), '%s', %d, %d)
  296. `, s.Time.Format(dtFormat), s.Measurement, s.ValueOne, s.ValueTwo)
  297. _, err = sess.Exec(sql)
  298. So(err, ShouldBeNil)
  299. }
  300. Convey("When doing a metric query grouping by time and select metric column should return correct series", func() {
  301. query := &tsdb.TsdbQuery{
  302. Queries: []*tsdb.Query{
  303. {
  304. Model: simplejson.NewFromAny(map[string]interface{}{
  305. "rawSql": "SELECT $__timeEpoch(time), measurement + ' - value one' as metric, valueOne FROM metric_values ORDER BY 1",
  306. "format": "time_series",
  307. }),
  308. RefId: "A",
  309. },
  310. },
  311. }
  312. resp, err := endpoint.Query(nil, nil, query)
  313. queryResult := resp.Results["A"]
  314. So(err, ShouldBeNil)
  315. So(queryResult.Error, ShouldBeNil)
  316. So(len(queryResult.Series), ShouldEqual, 2)
  317. So(queryResult.Series[0].Name, ShouldEqual, "Metric A - value one")
  318. So(queryResult.Series[1].Name, ShouldEqual, "Metric B - value one")
  319. })
  320. Convey("When doing a metric query grouping by time should return correct series", func() {
  321. query := &tsdb.TsdbQuery{
  322. Queries: []*tsdb.Query{
  323. {
  324. Model: simplejson.NewFromAny(map[string]interface{}{
  325. "rawSql": "SELECT $__timeEpoch(time), valueOne, valueTwo FROM metric_values ORDER BY 1",
  326. "format": "time_series",
  327. }),
  328. RefId: "A",
  329. },
  330. },
  331. }
  332. resp, err := endpoint.Query(nil, nil, query)
  333. queryResult := resp.Results["A"]
  334. So(err, ShouldBeNil)
  335. So(queryResult.Error, ShouldBeNil)
  336. So(len(queryResult.Series), ShouldEqual, 2)
  337. So(queryResult.Series[0].Name, ShouldEqual, "valueOne")
  338. So(queryResult.Series[1].Name, ShouldEqual, "valueTwo")
  339. })
  340. Convey("Given a stored procedure that takes @from and @to in epoch time", func() {
  341. sql := `
  342. IF object_id('sp_test_epoch') IS NOT NULL
  343. DROP PROCEDURE sp_test_epoch
  344. `
  345. _, err := sess.Exec(sql)
  346. So(err, ShouldBeNil)
  347. sql = `
  348. CREATE PROCEDURE sp_test_epoch(
  349. @from int,
  350. @to int
  351. ) AS
  352. BEGIN
  353. SELECT
  354. cast(cast(DATEDIFF(second, {d '1970-01-01'}, DATEADD(second, DATEDIFF(second,GETDATE(),GETUTCDATE()), time))/600 as int)*600 as int) as time,
  355. measurement + ' - value one' as metric,
  356. avg(valueOne) as value
  357. FROM
  358. metric_values
  359. WHERE
  360. time >= DATEADD(s, @from, '1970-01-01') AND time <= DATEADD(s, @to, '1970-01-01')
  361. GROUP BY
  362. cast(cast(DATEDIFF(second, {d '1970-01-01'}, DATEADD(second, DATEDIFF(second,GETDATE(),GETUTCDATE()), time))/600 as int)*600 as int),
  363. measurement
  364. UNION ALL
  365. SELECT
  366. cast(cast(DATEDIFF(second, {d '1970-01-01'}, DATEADD(second, DATEDIFF(second,GETDATE(),GETUTCDATE()), time))/600 as int)*600 as int) as time,
  367. measurement + ' - value two' as metric,
  368. avg(valueTwo) as value
  369. FROM
  370. metric_values
  371. WHERE
  372. time >= DATEADD(s, @from, '1970-01-01') AND time <= DATEADD(s, @to, '1970-01-01')
  373. GROUP BY
  374. cast(cast(DATEDIFF(second, {d '1970-01-01'}, DATEADD(second, DATEDIFF(second,GETDATE(),GETUTCDATE()), time))/600 as int)*600 as int),
  375. measurement
  376. ORDER BY 1
  377. END
  378. `
  379. _, err = sess.Exec(sql)
  380. So(err, ShouldBeNil)
  381. Convey("When doing a metric query using stored procedure should return correct result", func() {
  382. query := &tsdb.TsdbQuery{
  383. Queries: []*tsdb.Query{
  384. {
  385. Model: simplejson.NewFromAny(map[string]interface{}{
  386. "rawSql": `DECLARE
  387. @from int = $__unixEpochFrom(),
  388. @to int = $__unixEpochTo()
  389. EXEC dbo.sp_test_epoch @from, @to`,
  390. "format": "time_series",
  391. }),
  392. RefId: "A",
  393. },
  394. },
  395. TimeRange: &tsdb.TimeRange{
  396. From: "1521117000000",
  397. To: "1521122100000",
  398. },
  399. }
  400. resp, err := endpoint.Query(nil, nil, query)
  401. queryResult := resp.Results["A"]
  402. So(err, ShouldBeNil)
  403. So(queryResult.Error, ShouldBeNil)
  404. So(len(queryResult.Series), ShouldEqual, 4)
  405. So(queryResult.Series[0].Name, ShouldEqual, "Metric A - value one")
  406. So(queryResult.Series[1].Name, ShouldEqual, "Metric B - value one")
  407. So(queryResult.Series[2].Name, ShouldEqual, "Metric A - value two")
  408. So(queryResult.Series[3].Name, ShouldEqual, "Metric B - value two")
  409. })
  410. })
  411. Convey("Given a stored procedure that takes @from and @to in datetime", func() {
  412. sql := `
  413. IF object_id('sp_test_datetime') IS NOT NULL
  414. DROP PROCEDURE sp_test_datetime
  415. `
  416. _, err := sess.Exec(sql)
  417. So(err, ShouldBeNil)
  418. sql = `
  419. CREATE PROCEDURE sp_test_datetime(
  420. @from datetime,
  421. @to datetime
  422. ) AS
  423. BEGIN
  424. SELECT
  425. cast(cast(DATEDIFF(second, {d '1970-01-01'}, time)/600 as int)*600 as int) as time,
  426. measurement + ' - value one' as metric,
  427. avg(valueOne) as value
  428. FROM
  429. metric_values
  430. WHERE
  431. time >= @from AND time <= @to
  432. GROUP BY
  433. cast(cast(DATEDIFF(second, {d '1970-01-01'}, time)/600 as int)*600 as int),
  434. measurement
  435. UNION ALL
  436. SELECT
  437. cast(cast(DATEDIFF(second, {d '1970-01-01'}, time)/600 as int)*600 as int) as time,
  438. measurement + ' - value two' as metric,
  439. avg(valueTwo) as value
  440. FROM
  441. metric_values
  442. WHERE
  443. time >= @from AND time <= @to
  444. GROUP BY
  445. cast(cast(DATEDIFF(second, {d '1970-01-01'}, time)/600 as int)*600 as int),
  446. measurement
  447. ORDER BY 1
  448. END
  449. `
  450. _, err = sess.Exec(sql)
  451. So(err, ShouldBeNil)
  452. Convey("When doing a metric query using stored procedure should return correct result", func() {
  453. query := &tsdb.TsdbQuery{
  454. Queries: []*tsdb.Query{
  455. {
  456. Model: simplejson.NewFromAny(map[string]interface{}{
  457. "rawSql": `DECLARE
  458. @from int = $__unixEpochFrom(),
  459. @to int = $__unixEpochTo()
  460. EXEC dbo.sp_test_epoch @from, @to`,
  461. "format": "time_series",
  462. }),
  463. RefId: "A",
  464. },
  465. },
  466. TimeRange: &tsdb.TimeRange{
  467. From: "1521117000000",
  468. To: "1521122100000",
  469. },
  470. }
  471. resp, err := endpoint.Query(nil, nil, query)
  472. queryResult := resp.Results["A"]
  473. So(err, ShouldBeNil)
  474. So(queryResult.Error, ShouldBeNil)
  475. So(len(queryResult.Series), ShouldEqual, 4)
  476. So(queryResult.Series[0].Name, ShouldEqual, "Metric A - value one")
  477. So(queryResult.Series[1].Name, ShouldEqual, "Metric B - value one")
  478. So(queryResult.Series[2].Name, ShouldEqual, "Metric A - value two")
  479. So(queryResult.Series[3].Name, ShouldEqual, "Metric B - value two")
  480. })
  481. })
  482. })
  483. Convey("Given a table with event data", func() {
  484. sql := `
  485. IF OBJECT_ID('dbo.[event]', 'U') IS NOT NULL
  486. DROP TABLE dbo.[event]
  487. CREATE TABLE [event] (
  488. time_sec bigint,
  489. description nvarchar(100),
  490. tags nvarchar(100),
  491. )
  492. `
  493. _, err := sess.Exec(sql)
  494. So(err, ShouldBeNil)
  495. type event struct {
  496. TimeSec int64
  497. Description string
  498. Tags string
  499. }
  500. events := []*event{}
  501. for _, t := range genTimeRangeByInterval(fromStart.Add(-20*time.Minute), 60*time.Minute, 25*time.Minute) {
  502. events = append(events, &event{
  503. TimeSec: t.Unix(),
  504. Description: "Someone deployed something",
  505. Tags: "deploy",
  506. })
  507. events = append(events, &event{
  508. TimeSec: t.Add(5 * time.Minute).Unix(),
  509. Description: "New support ticket registered",
  510. Tags: "ticket",
  511. })
  512. }
  513. for _, e := range events {
  514. sql = fmt.Sprintf(`
  515. INSERT [event] (time_sec, description, tags)
  516. VALUES(%d, '%s', '%s')
  517. `, e.TimeSec, e.Description, e.Tags)
  518. _, err = sess.Exec(sql)
  519. So(err, ShouldBeNil)
  520. }
  521. Convey("When doing an annotation query of deploy events should return expected result", func() {
  522. query := &tsdb.TsdbQuery{
  523. Queries: []*tsdb.Query{
  524. {
  525. Model: simplejson.NewFromAny(map[string]interface{}{
  526. "rawSql": "SELECT time_sec as time, description as [text], tags FROM [event] WHERE $__unixEpochFilter(time_sec) AND tags='deploy' ORDER BY 1 ASC",
  527. "format": "table",
  528. }),
  529. RefId: "Deploys",
  530. },
  531. },
  532. TimeRange: &tsdb.TimeRange{
  533. From: fmt.Sprintf("%v", fromStart.Add(-20*time.Minute).Unix()*1000),
  534. To: fmt.Sprintf("%v", fromStart.Add(40*time.Minute).Unix()*1000),
  535. },
  536. }
  537. resp, err := endpoint.Query(nil, nil, query)
  538. queryResult := resp.Results["Deploys"]
  539. So(err, ShouldBeNil)
  540. So(len(queryResult.Tables[0].Rows), ShouldEqual, 3)
  541. })
  542. Convey("When doing an annotation query of ticket events should return expected result", func() {
  543. query := &tsdb.TsdbQuery{
  544. Queries: []*tsdb.Query{
  545. {
  546. Model: simplejson.NewFromAny(map[string]interface{}{
  547. "rawSql": "SELECT time_sec as time, description as [text], tags FROM [event] WHERE $__unixEpochFilter(time_sec) AND tags='ticket' ORDER BY 1 ASC",
  548. "format": "table",
  549. }),
  550. RefId: "Tickets",
  551. },
  552. },
  553. TimeRange: &tsdb.TimeRange{
  554. From: fmt.Sprintf("%v", fromStart.Add(-20*time.Minute).Unix()*1000),
  555. To: fmt.Sprintf("%v", fromStart.Add(40*time.Minute).Unix()*1000),
  556. },
  557. }
  558. resp, err := endpoint.Query(nil, nil, query)
  559. queryResult := resp.Results["Tickets"]
  560. So(err, ShouldBeNil)
  561. So(len(queryResult.Tables[0].Rows), ShouldEqual, 3)
  562. })
  563. })
  564. })
  565. }
  566. func InitMSSQLTestDB(t *testing.T) *xorm.Engine {
  567. x, err := xorm.NewEngine(sqlutil.TestDB_Mssql.DriverName, strings.Replace(sqlutil.TestDB_Mssql.ConnStr, "localhost", serverIP, 1))
  568. // x.ShowSQL()
  569. if err != nil {
  570. t.Fatalf("Failed to init mssql db %v", err)
  571. }
  572. sqlutil.CleanDB(x)
  573. return x
  574. }
  575. func genTimeRangeByInterval(from time.Time, duration time.Duration, interval time.Duration) []time.Time {
  576. durationSec := int64(duration.Seconds())
  577. intervalSec := int64(interval.Seconds())
  578. timeRange := []time.Time{}
  579. for i := int64(0); i < durationSec; i += intervalSec {
  580. timeRange = append(timeRange, from)
  581. from = from.Add(time.Duration(int64(time.Second) * intervalSec))
  582. }
  583. return timeRange
  584. }