2017-12-02 19:40:12 +08:00
package mssql
import (
"fmt"
"regexp"
"strings"
2018-03-14 05:49:49 +08:00
"time"
2017-12-02 19:40:12 +08:00
2018-03-15 20:11:26 +08:00
"strconv"
2018-03-19 20:32:04 +08:00
"github.com/grafana/grafana/pkg/tsdb"
2017-12-02 19:40:12 +08:00
)
const rsIdentifier = ` ([_a-zA-Z0-9]+) `
const sExpr = ` \$ ` + rsIdentifier + ` \(([^\)]*)\) `
2018-07-27 00:11:10 +08:00
type msSqlMacroEngine struct {
timeRange * tsdb . TimeRange
query * tsdb . Query
2017-12-02 19:40:12 +08:00
}
2018-07-27 00:11:10 +08:00
func newMssqlMacroEngine ( ) tsdb . SqlMacroEngine {
return & msSqlMacroEngine { }
2017-12-02 19:40:12 +08:00
}
2018-07-27 00:11:10 +08:00
func ( m * msSqlMacroEngine ) Interpolate ( query * tsdb . Query , timeRange * tsdb . TimeRange , sql string ) ( string , error ) {
m . timeRange = timeRange
m . query = query
2017-12-02 19:40:12 +08:00
rExp , _ := regexp . Compile ( sExpr )
var macroError error
sql = replaceAllStringSubmatchFunc ( rExp , sql , func ( groups [ ] string ) string {
2018-03-13 23:03:02 +08:00
args := strings . Split ( groups [ 2 ] , "," )
for i , arg := range args {
args [ i ] = strings . Trim ( arg , " " )
}
res , err := m . evaluateMacro ( groups [ 1 ] , args )
2017-12-02 19:40:12 +08:00
if err != nil && macroError == nil {
macroError = err
return "macro_error()"
}
return res
} )
if macroError != nil {
return "" , macroError
}
return sql , nil
}
func replaceAllStringSubmatchFunc ( re * regexp . Regexp , str string , repl func ( [ ] string ) string ) string {
result := ""
lastIndex := 0
for _ , v := range re . FindAllSubmatchIndex ( [ ] byte ( str ) , - 1 ) {
groups := [ ] string { }
for i := 0 ; i < len ( v ) ; i += 2 {
groups = append ( groups , str [ v [ i ] : v [ i + 1 ] ] )
}
result += str [ lastIndex : v [ 0 ] ] + repl ( groups )
lastIndex = v [ 1 ]
}
return result + str [ lastIndex : ]
}
2018-07-27 00:11:10 +08:00
func ( m * msSqlMacroEngine ) evaluateMacro ( name string , args [ ] string ) ( string , error ) {
2017-12-02 19:40:12 +08:00
switch name {
case "__time" :
if len ( args ) == 0 {
return "" , fmt . Errorf ( "missing time column argument for macro %v" , name )
}
2017-12-13 04:43:24 +08:00
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 )
}
2018-03-22 21:55:44 +08:00
return fmt . Sprintf ( "DATEDIFF(second, '1970-01-01', %s) AS time" , args [ 0 ] ) , nil
2017-12-02 19:40:12 +08:00
case "__timeFilter" :
if len ( args ) == 0 {
return "" , fmt . Errorf ( "missing time column argument for macro %v" , name )
}
2018-06-26 04:11:58 +08:00
2018-07-27 00:11:10 +08:00
return fmt . Sprintf ( "%s BETWEEN '%s' AND '%s'" , args [ 0 ] , m . timeRange . GetFromAsTimeUTC ( ) . Format ( time . RFC3339 ) , m . timeRange . GetToAsTimeUTC ( ) . Format ( time . RFC3339 ) ) , nil
2017-12-02 19:40:12 +08:00
case "__timeFrom" :
2018-07-27 00:11:10 +08:00
return fmt . Sprintf ( "'%s'" , m . timeRange . GetFromAsTimeUTC ( ) . Format ( time . RFC3339 ) ) , nil
2017-12-02 19:40:12 +08:00
case "__timeTo" :
2018-07-27 00:11:10 +08:00
return fmt . Sprintf ( "'%s'" , m . timeRange . GetToAsTimeUTC ( ) . Format ( time . RFC3339 ) ) , nil
2018-03-14 05:49:49 +08:00
case "__timeGroup" :
if len ( args ) < 2 {
return "" , fmt . Errorf ( "macro %v needs time column and interval" , name )
}
interval , err := time . ParseDuration ( strings . Trim ( args [ 1 ] , ` '" ` ) )
if err != nil {
return "" , fmt . Errorf ( "error parsing interval %v" , args [ 1 ] )
}
2018-03-15 20:11:26 +08:00
if len ( args ) == 3 {
2018-07-27 00:11:10 +08:00
m . query . Model . Set ( "fill" , true )
m . query . Model . Set ( "fillInterval" , interval . Seconds ( ) )
2018-07-30 17:04:04 +08:00
switch args [ 2 ] {
case "NULL" :
m . query . Model . Set ( "fillMode" , "null" )
case "last" :
m . query . Model . Set ( "fillMode" , "last" )
default :
m . query . Model . Set ( "fillMode" , "value" )
2018-03-15 20:11:26 +08:00
floatVal , err := strconv . ParseFloat ( args [ 2 ] , 64 )
if err != nil {
return "" , fmt . Errorf ( "error parsing fill value %v" , args [ 2 ] )
}
2018-07-27 00:11:10 +08:00
m . query . Model . Set ( "fillValue" , floatVal )
2018-03-15 20:11:26 +08:00
}
}
2018-07-01 21:57:02 +08:00
return fmt . Sprintf ( "FLOOR(DATEDIFF(second, '1970-01-01', %s)/%.0f)*%.0f" , args [ 0 ] , interval . Seconds ( ) , interval . Seconds ( ) ) , nil
2018-08-02 02:58:51 +08:00
case "__timeGroupAlias" :
tg , err := m . evaluateMacro ( "__timeGroup" , args )
if err == nil {
return tg + " AS [time]" , err
}
return "" , err
2017-12-02 19:40:12 +08:00
case "__unixEpochFilter" :
if len ( args ) == 0 {
return "" , fmt . Errorf ( "missing time column argument for macro %v" , name )
}
2018-07-27 00:11:10 +08:00
return fmt . Sprintf ( "%s >= %d AND %s <= %d" , args [ 0 ] , m . timeRange . GetFromAsSecondsEpoch ( ) , args [ 0 ] , m . timeRange . GetToAsSecondsEpoch ( ) ) , nil
2017-12-02 19:40:12 +08:00
case "__unixEpochFrom" :
2018-07-27 00:11:10 +08:00
return fmt . Sprintf ( "%d" , m . timeRange . GetFromAsSecondsEpoch ( ) ) , nil
2017-12-02 19:40:12 +08:00
case "__unixEpochTo" :
2018-07-27 00:11:10 +08:00
return fmt . Sprintf ( "%d" , m . timeRange . GetToAsSecondsEpoch ( ) ) , nil
2017-12-02 19:40:12 +08:00
default :
return "" , fmt . Errorf ( "Unknown macro %v" , name )
}
}