2017-04-19 16:09:52 +08:00
package mysql
import (
"fmt"
"regexp"
2017-10-27 17:26:25 +08:00
"strings"
2017-04-19 16:09:52 +08:00
2021-09-07 15:35:37 +08:00
"github.com/grafana/grafana-plugin-sdk-go/backend"
2021-09-21 19:08:52 +08:00
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
2023-12-14 16:42:06 +08:00
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
2019-09-11 03:50:04 +08:00
"github.com/grafana/grafana/pkg/tsdb/sqleng"
2017-04-19 16:09:52 +08:00
)
const rsIdentifier = ` ([_a-zA-Z0-9]+) `
2017-04-20 17:59:11 +08:00
const sExpr = ` \$ ` + rsIdentifier + ` \(([^\)]*)\) `
2017-04-19 16:09:52 +08:00
2019-09-25 02:50:49 +08:00
var restrictedRegExp = regexp . MustCompile ( ` (?im)([\s]*show[\s]+grants|[\s,]session_user\([^\)]*\)|[\s,]current_user(\([^\)]*\))?|[\s,]system_user\([^\)]*\)|[\s,]user\([^\)]*\))([\s,;]|$) ` )
2021-03-08 14:02:49 +08:00
type mySQLMacroEngine struct {
* sqleng . SQLMacroEngineBase
2023-06-16 23:46:47 +08:00
logger log . Logger
userError string
2017-04-19 16:09:52 +08:00
}
2024-02-22 21:22:14 +08:00
func newMysqlMacroEngine ( logger log . Logger , userFacingDefaultError string ) sqleng . SQLMacroEngine {
2023-06-16 23:46:47 +08:00
return & mySQLMacroEngine {
SQLMacroEngineBase : sqleng . NewSQLMacroEngineBase ( ) ,
logger : logger ,
2024-02-22 21:22:14 +08:00
userError : userFacingDefaultError ,
2023-06-16 23:46:47 +08:00
}
2017-04-19 16:09:52 +08:00
}
2021-09-07 15:35:37 +08:00
func ( m * mySQLMacroEngine ) Interpolate ( query * backend . DataQuery , timeRange backend . TimeRange , sql string ) ( string , error ) {
2019-09-25 02:50:49 +08:00
matches := restrictedRegExp . FindAllStringSubmatch ( sql , 1 )
if len ( matches ) > 0 {
2023-09-05 04:25:43 +08:00
m . logger . Error ( "Show grants, session_user(), current_user(), system_user() or user() not allowed in query" )
2023-06-16 23:46:47 +08:00
return "" , fmt . Errorf ( "invalid query - %s" , m . userError )
2019-09-25 02:50:49 +08:00
}
2021-03-08 14:02:49 +08:00
// TODO: Handle error
2017-04-19 16:09:52 +08:00
rExp , _ := regexp . Compile ( sExpr )
var macroError error
2018-09-13 22:51:00 +08:00
sql = m . ReplaceAllStringSubmatchFunc ( rExp , sql , func ( groups [ ] string ) string {
2018-03-03 02:24:15 +08:00
args := strings . Split ( groups [ 2 ] , "," )
for i , arg := range args {
args [ i ] = strings . Trim ( arg , " " )
}
2021-06-11 20:56:29 +08:00
res , err := m . evaluateMacro ( timeRange , query , groups [ 1 ] , args )
2017-04-19 23:26:29 +08:00
if err != nil && macroError == nil {
2017-04-19 16:09:52 +08:00
macroError = err
return "macro_error()"
}
return res
} )
if macroError != nil {
return "" , macroError
}
return sql , nil
}
2021-09-07 15:35:37 +08:00
func ( m * mySQLMacroEngine ) evaluateMacro ( timeRange backend . TimeRange , query * backend . DataQuery , name string , args [ ] string ) ( string , error ) {
2017-04-19 16:09:52 +08:00
switch name {
2018-03-22 22:40:46 +08:00
case "__timeEpoch" , "__time" :
2017-04-19 16:09:52 +08:00
if len ( args ) == 0 {
return "" , fmt . Errorf ( "missing time column argument for macro %v" , name )
}
2017-04-19 23:26:29 +08:00
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 )
}
2022-02-23 03:06:04 +08:00
if timeRange . From . UTC ( ) . Unix ( ) < 0 {
return fmt . Sprintf ( "%s BETWEEN DATE_ADD(FROM_UNIXTIME(0), INTERVAL %d SECOND) AND FROM_UNIXTIME(%d)" , args [ 0 ] , timeRange . From . UTC ( ) . Unix ( ) , timeRange . To . UTC ( ) . Unix ( ) ) , nil
}
2021-09-07 15:35:37 +08:00
return fmt . Sprintf ( "%s BETWEEN FROM_UNIXTIME(%d) AND FROM_UNIXTIME(%d)" , args [ 0 ] , timeRange . From . UTC ( ) . Unix ( ) , timeRange . To . UTC ( ) . Unix ( ) ) , nil
2018-11-30 21:18:33 +08:00
case "__timeFrom" :
2021-09-07 15:35:37 +08:00
return fmt . Sprintf ( "FROM_UNIXTIME(%d)" , timeRange . From . UTC ( ) . Unix ( ) ) , nil
2018-11-30 21:18:33 +08:00
case "__timeTo" :
2021-09-07 15:35:37 +08:00
return fmt . Sprintf ( "FROM_UNIXTIME(%d)" , timeRange . To . UTC ( ) . Unix ( ) ) , nil
2017-10-27 17:26:25 +08:00
case "__timeGroup" :
2017-12-11 02:50:01 +08:00
if len ( args ) < 2 {
2017-10-27 17:26:25 +08:00
return "" , fmt . Errorf ( "macro %v needs time column and interval" , name )
}
2019-05-30 15:58:29 +08:00
interval , err := gtime . ParseInterval ( strings . Trim ( args [ 1 ] , ` '" ` ) )
2017-10-27 17:26:25 +08:00
if err != nil {
return "" , fmt . Errorf ( "error parsing interval %v" , args [ 1 ] )
}
2017-12-11 02:50:01 +08:00
if len ( args ) == 3 {
2021-06-11 20:56:29 +08:00
err := sqleng . SetupFillmode ( query , interval , args [ 2 ] )
2018-08-12 16:51:58 +08:00
if err != nil {
return "" , err
2017-12-11 02:50:01 +08:00
}
}
2018-07-01 21:59:05 +08:00
return fmt . Sprintf ( "UNIX_TIMESTAMP(%s) DIV %.0f * %.0f" , args [ 0 ] , interval . Seconds ( ) , interval . Seconds ( ) ) , nil
2018-08-02 02:58:51 +08:00
case "__timeGroupAlias" :
2021-06-11 20:56:29 +08:00
tg , err := m . evaluateMacro ( timeRange , query , "__timeGroup" , args )
2018-08-02 02:58:51 +08:00
if err == nil {
2020-11-17 18:27:45 +08:00
return tg + " AS \"time\"" , nil
2018-08-02 02:58:51 +08:00
}
return "" , err
2017-06-09 05:36:05 +08:00
case "__unixEpochFilter" :
if len ( args ) == 0 {
return "" , fmt . Errorf ( "missing time column argument for macro %v" , name )
}
2021-09-07 15:35:37 +08:00
return fmt . Sprintf ( "%s >= %d AND %s <= %d" , args [ 0 ] , timeRange . From . UTC ( ) . Unix ( ) , args [ 0 ] , timeRange . To . UTC ( ) . Unix ( ) ) , nil
2019-01-04 05:29:57 +08:00
case "__unixEpochNanoFilter" :
if len ( args ) == 0 {
return "" , fmt . Errorf ( "missing time column argument for macro %v" , name )
}
2021-09-07 15:35:37 +08:00
return fmt . Sprintf ( "%s >= %d AND %s <= %d" , args [ 0 ] , timeRange . From . UTC ( ) . UnixNano ( ) , args [ 0 ] , timeRange . To . UTC ( ) . UnixNano ( ) ) , nil
2019-01-04 05:29:57 +08:00
case "__unixEpochNanoFrom" :
2021-09-07 15:35:37 +08:00
return fmt . Sprintf ( "%d" , timeRange . From . UTC ( ) . UnixNano ( ) ) , nil
2019-01-04 05:29:57 +08:00
case "__unixEpochNanoTo" :
2021-09-07 15:35:37 +08:00
return fmt . Sprintf ( "%d" , timeRange . To . UTC ( ) . UnixNano ( ) ) , nil
2018-08-13 18:17:05 +08:00
case "__unixEpochGroup" :
if len ( args ) < 2 {
return "" , fmt . Errorf ( "macro %v needs time column and interval and optional fill value" , name )
}
2019-05-30 15:58:29 +08:00
interval , err := gtime . ParseInterval ( strings . Trim ( args [ 1 ] , ` ' ` ) )
2018-08-13 18:17:05 +08:00
if err != nil {
return "" , fmt . Errorf ( "error parsing interval %v" , args [ 1 ] )
}
if len ( args ) == 3 {
2021-06-11 20:56:29 +08:00
err := sqleng . SetupFillmode ( query , interval , args [ 2 ] )
2018-08-13 18:17:05 +08:00
if err != nil {
return "" , err
}
}
return fmt . Sprintf ( "%s DIV %v * %v" , args [ 0 ] , interval . Seconds ( ) , interval . Seconds ( ) ) , nil
case "__unixEpochGroupAlias" :
2021-06-11 20:56:29 +08:00
tg , err := m . evaluateMacro ( timeRange , query , "__unixEpochGroup" , args )
2018-08-13 18:17:05 +08:00
if err == nil {
2020-11-17 18:27:45 +08:00
return tg + " AS \"time\"" , nil
2018-08-13 18:17:05 +08:00
}
return "" , err
2017-04-19 16:09:52 +08:00
default :
2020-11-05 18:29:39 +08:00
return "" , fmt . Errorf ( "unknown macro %v" , name )
2017-04-19 16:09:52 +08:00
}
}