diff --git a/pkg/tsdb/models.go b/pkg/tsdb/models.go index 838767dd5d9..0757a0f33b3 100644 --- a/pkg/tsdb/models.go +++ b/pkg/tsdb/models.go @@ -45,10 +45,11 @@ func (br *BatchResult) WithError(err error) *BatchResult { } type QueryResult struct { - Error error `json:"-"` - ErrorString string `json:"error"` - RefId string `json:"refId"` - Series TimeSeriesSlice `json:"series"` + Error error `json:"-"` + ErrorString string `json:"error,omitempty"` + RefId string `json:"refId"` + Meta *simplejson.Json `json:"meta,omitempty"` + Series TimeSeriesSlice `json:"series"` } type TimeSeries struct { diff --git a/pkg/tsdb/mysql/macros.go b/pkg/tsdb/mysql/macros.go index 971b41c3b07..5577819d784 100644 --- a/pkg/tsdb/mysql/macros.go +++ b/pkg/tsdb/mysql/macros.go @@ -30,8 +30,8 @@ func (m *MySqlMacroEngine) Interpolate(sql string) (string, error) { var macroError error sql = ReplaceAllStringSubmatchFunc(rExp, sql, func(groups []string) string { - res, err := m.EvaluateMacro(groups[1], groups[2:len(groups)]) - if macroError != nil { + res, err := m.EvaluateMacro(groups[1], groups[2:]) + if err != nil && macroError == nil { macroError = err return "macro_error()" } @@ -68,7 +68,12 @@ func (m *MySqlMacroEngine) EvaluateMacro(name string, args []string) (string, er if len(args) == 0 { return "", fmt.Errorf("missing time column argument for macro %v", name) } - return "UNIX_TIMESTAMP(" + args[0] + ") as time_sec", nil + return fmt.Sprintf("UNIX_TIMESTAMP(%s) as time_sec", args[0]), nil + case "__timeFilter": + if len(args) == 0 { + return "", fmt.Errorf("missing time column argument for macro %v", name) + } + return fmt.Sprintf("UNIX_TIMESTAMP(%s) > %d AND UNIX_TIMESTAMP(%s) < %d", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil default: return "", fmt.Errorf("Unknown macro %v", name) } diff --git a/pkg/tsdb/mysql/macros_test.go b/pkg/tsdb/mysql/macros_test.go index 1dcd5e1e978..a1af3caf62d 100644 --- a/pkg/tsdb/mysql/macros_test.go +++ b/pkg/tsdb/mysql/macros_test.go @@ -3,13 +3,14 @@ package mysql import ( "testing" + "github.com/grafana/grafana/pkg/tsdb" . "github.com/smartystreets/goconvey/convey" ) func TestMacroEngine(t *testing.T) { Convey("MacroEngine", t, func() { - Convey("interpolate simple function", func() { + Convey("interpolate __time function", func() { engine := &MySqlMacroEngine{} sql, err := engine.Interpolate("select $__time(time_column)") @@ -18,5 +19,16 @@ func TestMacroEngine(t *testing.T) { So(sql, ShouldEqual, "select UNIX_TIMESTAMP(time_column) as time_sec") }) + Convey("interpolate __timeFilter function", func() { + engine := &MySqlMacroEngine{ + TimeRange: &tsdb.TimeRange{From: "5m", To: "now"}, + } + + sql, err := engine.Interpolate("WHERE $__timeFilter(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "WHERE UNIX_TIMESTAMP(time_column) > 18446744066914186738 AND UNIX_TIMESTAMP(time_column) < 18446744066914187038") + }) + }) } diff --git a/pkg/tsdb/mysql/mysql.go b/pkg/tsdb/mysql/mysql.go index 44dcc2648f2..fd00cdff47e 100644 --- a/pkg/tsdb/mysql/mysql.go +++ b/pkg/tsdb/mysql/mysql.go @@ -10,6 +10,7 @@ import ( "github.com/go-xorm/core" "github.com/go-xorm/xorm" "github.com/grafana/grafana/pkg/components/null" + "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/tsdb" @@ -81,6 +82,7 @@ func (e *MysqlExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, co QueryResults: make(map[string]*tsdb.QueryResult), } + macroEngine := NewMysqlMacroEngine(context.TimeRange) session := e.engine.NewSession() defer session.Close() db := session.DB() @@ -91,9 +93,20 @@ func (e *MysqlExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, co continue } + queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: query.RefId} + result.QueryResults[query.RefId] = queryResult + + rawSql, err := macroEngine.Interpolate(rawSql) + if err != nil { + queryResult.Error = err + continue + } + + queryResult.Meta.Set("sql", rawSql) + rows, err := db.Query(rawSql) if err != nil { - result.QueryResults[query.RefId] = &tsdb.QueryResult{Error: err} + queryResult.Error = err continue } @@ -101,16 +114,24 @@ func (e *MysqlExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, co res, err := e.TransformToTimeSeries(query, rows) if err != nil { - result.Error = err + queryResult.Error = err return result } - result.QueryResults[query.RefId] = &tsdb.QueryResult{RefId: query.RefId, Series: res} + queryResult.Series = res + queryResult.Meta.Set("rowCount", countPointsInAllSeries(res)) } return result } +func countPointsInAllSeries(seriesList tsdb.TimeSeriesSlice) (count int) { + for _, series := range seriesList { + count += len(series.Points) + } + return count +} + func (e MysqlExecutor) TransformToTimeSeries(query *tsdb.Query, rows *core.Rows) (tsdb.TimeSeriesSlice, error) { pointsBySeries := make(map[string]*tsdb.TimeSeries) columnNames, err := rows.Columns()