mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 15:02:33 +08:00
Sql Expressions: State when error is from GMS (#102112)
This commit is contained in:
@ -4,6 +4,7 @@ package sql
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
sqle "github.com/dolthub/go-mysql-server"
|
sqle "github.com/dolthub/go-mysql-server"
|
||||||
mysql "github.com/dolthub/go-mysql-server/sql"
|
mysql "github.com/dolthub/go-mysql-server/sql"
|
||||||
@ -15,6 +16,42 @@ import (
|
|||||||
// DB is a database that can execute SQL queries against a set of Frames.
|
// DB is a database that can execute SQL queries against a set of Frames.
|
||||||
type DB struct{}
|
type DB struct{}
|
||||||
|
|
||||||
|
// GoMySQLServerError represents an error from the underlying Go MySQL Server
|
||||||
|
type GoMySQLServerError struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements the error interface
|
||||||
|
func (e *GoMySQLServerError) Error() string {
|
||||||
|
return fmt.Sprintf("error in go-mysql-server: %v", e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap provides the original error for errors.Is/As
|
||||||
|
func (e *GoMySQLServerError) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapGoMySQLServerError wraps errors from Go MySQL Server with additional context
|
||||||
|
func WrapGoMySQLServerError(err error) error {
|
||||||
|
// Don't wrap nil errors
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's a function not found error or other specific GMS errors
|
||||||
|
if isFunctionNotFoundError(err) {
|
||||||
|
return &GoMySQLServerError{Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return original error if it's not one we want to wrap
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// isFunctionNotFoundError checks if the error is related to a function not being found
|
||||||
|
func isFunctionNotFoundError(err error) bool {
|
||||||
|
return mysql.ErrFunctionNotFound.Is(err)
|
||||||
|
}
|
||||||
|
|
||||||
// QueryFrames runs the sql query query against a database created from frames, and returns the frame.
|
// QueryFrames runs the sql query query against a database created from frames, and returns the frame.
|
||||||
// The RefID of each frame becomes a table in the database.
|
// The RefID of each frame becomes a table in the database.
|
||||||
// It is expected that there is only one frame per RefID.
|
// It is expected that there is only one frame per RefID.
|
||||||
@ -47,7 +84,7 @@ func (db *DB) QueryFrames(ctx context.Context, name string, query string, frames
|
|||||||
|
|
||||||
schema, iter, _, err := engine.Query(mCtx, query)
|
schema, iter, _, err := engine.Query(mCtx, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, WrapGoMySQLServerError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := convertToDataFrame(mCtx, iter, schema)
|
f, err := convertToDataFrame(mCtx, iter, schema)
|
||||||
|
@ -193,6 +193,18 @@ func TestQueryFramesDateTimeSelect(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestErrorsFromGoMySQLServerAreFlagged(t *testing.T) {
|
||||||
|
const GmsNotImplemented = "STDDEV" // not implemented in go-mysql-server as of 2025-03-18
|
||||||
|
|
||||||
|
db := DB{}
|
||||||
|
|
||||||
|
query := `SELECT ` + GmsNotImplemented + `(1);`
|
||||||
|
|
||||||
|
_, err := db.QueryFrames(context.Background(), "sqlExpressionRefId", query, nil)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "error in go-mysql-server")
|
||||||
|
}
|
||||||
|
|
||||||
// p is a utility for pointers from constants
|
// p is a utility for pointers from constants
|
||||||
func p[T any](v T) *T {
|
func p[T any](v T) *T {
|
||||||
return &v
|
return &v
|
||||||
|
@ -129,7 +129,17 @@ var example_case_statement = `SELECT
|
|||||||
END AS category
|
END AS category
|
||||||
FROM metrics`
|
FROM metrics`
|
||||||
|
|
||||||
var example_all_allowed_functions = `SELECT
|
var example_all_allowed_functions = `WITH sample_data AS (
|
||||||
|
SELECT
|
||||||
|
100 AS value,
|
||||||
|
'example' AS name,
|
||||||
|
NOW() AS created_at
|
||||||
|
UNION ALL SELECT
|
||||||
|
50 AS value,
|
||||||
|
'test' AS name,
|
||||||
|
DATE_SUB(NOW(), INTERVAL 1 DAY) AS created_at
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
-- Conditional functions
|
-- Conditional functions
|
||||||
IF(value > 100, 'High', 'Low') AS conditional_if,
|
IF(value > 100, 'High', 'Low') AS conditional_if,
|
||||||
COALESCE(value, 0) AS conditional_coalesce,
|
COALESCE(value, 0) AS conditional_coalesce,
|
||||||
@ -191,6 +201,6 @@ var example_all_allowed_functions = `SELECT
|
|||||||
-- Type conversion
|
-- Type conversion
|
||||||
CAST(value AS CHAR) AS type_cast,
|
CAST(value AS CHAR) AS type_cast,
|
||||||
CONVERT(value, CHAR) AS type_convert
|
CONVERT(value, CHAR) AS type_convert
|
||||||
FROM metrics
|
FROM sample_data
|
||||||
GROUP BY name, value, created_at
|
GROUP BY name, value, created_at
|
||||||
LIMIT 10`
|
LIMIT 10`
|
||||||
|
Reference in New Issue
Block a user