SQL Expressions: Rework backend errors and error instrumentation (#109633)
* Capture error_type label on metrics/traces * Make error messages more helpful to user * Use errutil, categorized errors, and tie them to error_type (category in code) * Misc trace fixes * Add metric to track SQL input conversion
This commit is contained in:
+4
-40
@@ -19,42 +19,6 @@ import (
|
||||
// DB is a database that can execute SQL queries against a set of Frames.
|
||||
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)
|
||||
}
|
||||
|
||||
type QueryOption func(*QueryOptions)
|
||||
|
||||
type QueryOptions struct {
|
||||
@@ -80,7 +44,7 @@ func WithMaxOutputCells(n int64) QueryOption {
|
||||
// The name becomes the name and RefID of the returned frame.
|
||||
func (db *DB) QueryFrames(ctx context.Context, tracer tracing.Tracer, name string, query string, frames []*data.Frame, opts ...QueryOption) (*data.Frame, error) {
|
||||
// We are parsing twice due to TablesList, but don't care fow now. We can save the parsed query and reuse it later if we want.
|
||||
if allow, err := AllowQuery(query); err != nil || !allow {
|
||||
if allow, err := AllowQuery(name, query); err != nil || !allow {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -122,9 +86,9 @@ func (db *DB) QueryFrames(ctx context.Context, tracer tracing.Tracer, name strin
|
||||
contextErr := func(err error) error {
|
||||
switch {
|
||||
case errors.Is(err, context.DeadlineExceeded):
|
||||
return fmt.Errorf("SQL expression for refId %v did not complete within the timeout of %v: %w", name, QueryOptions.Timeout, err)
|
||||
return MakeTimeOutError(err, name, QueryOptions.Timeout)
|
||||
case errors.Is(err, context.Canceled):
|
||||
return fmt.Errorf("SQL expression for refId %v was cancelled before it completed: %w", name, err)
|
||||
return MakeCancelError(err, name)
|
||||
default:
|
||||
return fmt.Errorf("SQL expression for refId %v ended unexpectedly: %w", name, err)
|
||||
}
|
||||
@@ -136,7 +100,7 @@ func (db *DB) QueryFrames(ctx context.Context, tracer tracing.Tracer, name strin
|
||||
if ctx.Err() != nil {
|
||||
return nil, contextErr(ctx.Err())
|
||||
}
|
||||
return nil, WrapGoMySQLServerError(err)
|
||||
return nil, MakeGMSError(name, err)
|
||||
}
|
||||
|
||||
// Convert the iterator into a Grafana data.Frame
|
||||
|
||||
Reference in New Issue
Block a user