Files
grafana/pkg/expr/sql/parser_allow.go
Kyle Brandt d64f41afdc SQL Expressions: Re-implement feature using go-mysql-server (#99521)
* Under feature flag `sqlExpressions` and is experimental
* Excluded from arm32
* Will not work with the Query Service yet
* Does not have limits in place yet
* Does not working with alerting yet
* Currently requires "prepare time series" Transform for time series viz
 
---------

Co-authored-by: Sam Jewell <sam.jewell@grafana.com>
2025-02-06 07:27:28 -05:00

137 lines
2.5 KiB
Go

package sql
import (
"fmt"
"strings"
"github.com/dolthub/vitess/go/vt/sqlparser"
)
// AllowQuery parses the query and checks it against an allow list of allowed SQL nodes
// and functions.
func AllowQuery(rawSQL string) (bool, error) {
s, err := sqlparser.Parse(rawSQL)
if err != nil {
return false, fmt.Errorf("error parsing sql: %s", err.Error())
}
walkSubtree := func(node sqlparser.SQLNode) error {
err := sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) {
if !allowedNode(node) {
if fT, ok := node.(*sqlparser.FuncExpr); ok {
return false, fmt.Errorf("blocked function %s - not supported in queries", fT.Name)
}
return false, fmt.Errorf("blocked node %T - not supported in queries", node)
}
return true, nil
}, node)
if err != nil {
return fmt.Errorf("failed to parse SQL expression: %w", err)
}
return nil
}
if err := walkSubtree(s); err != nil {
return false, err
}
return true, nil
}
// nolint:gocyclo,nakedret
func allowedNode(node sqlparser.SQLNode) (b bool) {
b = true // so don't have to return true in every case but default
switch v := node.(type) {
case *sqlparser.FuncExpr:
return allowedFunction(v)
case *sqlparser.AsOf:
return
case *sqlparser.AliasedExpr, *sqlparser.AliasedTableExpr:
return
case *sqlparser.BinaryExpr:
return
case sqlparser.ColIdent, *sqlparser.ColName, sqlparser.Columns:
return
case sqlparser.Comments: // TODO: understand why some are pointer vs not
return
case *sqlparser.CommonTableExpr:
return
case *sqlparser.ComparisonExpr:
return
case *sqlparser.ConvertExpr:
return
case sqlparser.GroupBy:
return
case *sqlparser.IndexHints:
return
case *sqlparser.Into:
return
case *sqlparser.JoinTableExpr, sqlparser.JoinCondition:
return
case *sqlparser.Select, sqlparser.SelectExprs:
return
case *sqlparser.StarExpr:
return
case *sqlparser.SQLVal:
return
case *sqlparser.Limit:
return
case *sqlparser.Order, sqlparser.OrderBy:
return
case *sqlparser.Over:
return
case *sqlparser.Subquery:
return
case sqlparser.TableName, sqlparser.TableExprs, sqlparser.TableIdent:
return
case *sqlparser.With:
return
case *sqlparser.Where:
return
default:
return false
}
}
// nolint:gocyclo,nakedret
func allowedFunction(f *sqlparser.FuncExpr) (b bool) {
b = true // so don't have to return true in every case but default
switch strings.ToLower(f.Name.String()) {
case "sum", "avg", "count", "min", "max":
return
case "coalesce":
return
default:
return false
}
}