The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/tsdb/mssql/macros.go

132 lines
4.2 KiB

package mssql
import (
"fmt"
"regexp"
"strings"
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
"github.com/grafana/grafana/pkg/tsdb/sqleng"
)
const rsIdentifier = `([_a-zA-Z0-9]+)`
const sExpr = `\$` + rsIdentifier + `\(([^\)]*)\)`
type msSQLMacroEngine struct {
*sqleng.SQLMacroEngineBase
}
func newMssqlMacroEngine() sqleng.SQLMacroEngine {
return &msSQLMacroEngine{SQLMacroEngineBase: sqleng.NewSQLMacroEngineBase()}
}
func (m *msSQLMacroEngine) Interpolate(query *backend.DataQuery, timeRange backend.TimeRange,
sql string) (string, error) {
// TODO: Return any error
rExp, _ := regexp.Compile(sExpr)
var macroError error
sql = m.ReplaceAllStringSubmatchFunc(rExp, sql, func(groups []string) string {
args := strings.Split(groups[2], ",")
for i, arg := range args {
args[i] = strings.Trim(arg, " ")
}
res, err := m.evaluateMacro(timeRange, query, groups[1], args)
if err != nil && macroError == nil {
macroError = err
return "macro_error()"
}
return res
})
if macroError != nil {
return "", macroError
}
return sql, nil
}
func (m *msSQLMacroEngine) evaluateMacro(timeRange backend.TimeRange, query *backend.DataQuery, name string, args []string) (string, error) {
switch name {
case "__time":
if len(args) == 0 {
return "", fmt.Errorf("missing time column argument for macro %v", name)
}
return fmt.Sprintf("%s AS time", args[0]), nil
case "__timeEpoch":
if len(args) == 0 {
return "", fmt.Errorf("missing time column argument for macro %v", name)
}
return fmt.Sprintf("DATEDIFF(second, '1970-01-01', %s) AS time", args[0]), nil
case "__timeFilter":
if len(args) == 0 {
return "", fmt.Errorf("missing time column argument for macro %v", name)
}
return fmt.Sprintf("%s BETWEEN '%s' AND '%s'", args[0], timeRange.From.UTC().Format(time.RFC3339), timeRange.To.UTC().Format(time.RFC3339)), nil
case "__timeFrom":
return fmt.Sprintf("'%s'", timeRange.From.UTC().Format(time.RFC3339)), nil
case "__timeTo":
return fmt.Sprintf("'%s'", timeRange.To.UTC().Format(time.RFC3339)), nil
case "__timeGroup":
if len(args) < 2 {
return "", fmt.Errorf("macro %v needs time column and interval", name)
}
interval, err := gtime.ParseInterval(strings.Trim(args[1], `'"`))
if err != nil {
return "", fmt.Errorf("error parsing interval %v", args[1])
}
if len(args) == 3 {
err := sqleng.SetupFillmode(query, interval, args[2])
if err != nil {
return "", err
}
}
return fmt.Sprintf("FLOOR(DATEDIFF(second, '1970-01-01', %s)/%.0f)*%.0f", args[0], interval.Seconds(), interval.Seconds()), nil
case "__timeGroupAlias":
tg, err := m.evaluateMacro(timeRange, query, "__timeGroup", args)
if err == nil {
return tg + " AS [time]", nil
}
return "", err
case "__unixEpochFilter":
if len(args) == 0 {
return "", fmt.Errorf("missing time column argument for macro %v", name)
}
return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], timeRange.From.UTC().Unix(), args[0], timeRange.To.UTC().Unix()), nil
case "__unixEpochNanoFilter":
if len(args) == 0 {
return "", fmt.Errorf("missing time column argument for macro %v", name)
}
return fmt.Sprintf("%s >= %d AND %s <= %d", args[0], timeRange.From.UTC().UnixNano(), args[0], timeRange.To.UTC().UnixNano()), nil
case "__unixEpochNanoFrom":
return fmt.Sprintf("%d", timeRange.From.UTC().UnixNano()), nil
case "__unixEpochNanoTo":
return fmt.Sprintf("%d", timeRange.To.UTC().UnixNano()), nil
case "__unixEpochGroup":
if len(args) < 2 {
return "", fmt.Errorf("macro %v needs time column and interval and optional fill value", name)
}
interval, err := gtime.ParseInterval(strings.Trim(args[1], `'`))
if err != nil {
return "", fmt.Errorf("error parsing interval %v", args[1])
}
if len(args) == 3 {
err := sqleng.SetupFillmode(query, interval, args[2])
if err != nil {
return "", err
}
}
return fmt.Sprintf("FLOOR(%s/%v)*%v", args[0], interval.Seconds(), interval.Seconds()), nil
case "__unixEpochGroupAlias":
tg, err := m.evaluateMacro(timeRange, query, "__unixEpochGroup", args)
if err == nil {
return tg + " AS [time]", nil
}
return "", err
default:
return "", fmt.Errorf("unknown macro %q", name)
}
}