@@ -74,55 +74,51 @@ func NewQuery(db Connection, settings backend.DataSourceInstanceSettings, conver
7474}
7575
7676// Run sends the query to the connection and converts the rows to a dataframe.
77- func (q * DBQuery ) Run (ctx context.Context , query * Query , args ... interface {}) (data.Frames , error ) {
77+ func (q * DBQuery ) Run (ctx context.Context , query * Query , queryErrorMutator QueryErrorMutator , args ... interface {}) (data.Frames , error ) {
7878 start := time .Now ()
7979 rows , err := q .DB .QueryContext (ctx , query .RawSQL , args ... )
8080 if err != nil {
81- // Determine error source based on retry configuration and error type
82- errSource := backend .ErrorSourcePlugin
83- errType := ErrorQuery
81+ var errWithSource backend.ErrorWithSource
82+ defer func () {
83+ q .metrics .CollectDuration (Source (errWithSource .ErrorSource ()), StatusError , time .Since (start ).Seconds ())
84+ }()
8485
8586 if errors .Is (err , context .Canceled ) {
86- errType = context .Canceled
87- errSource = backend .ErrorSourceDownstream
88- } else if IsPGXConnectionError (err ) {
89- errType = ErrorPGXLifecycle
90- errSource = backend .ErrorSourceDownstream
91- } else {
92- // Use enhanced error classification for PGX v5
93- errSource , _ = ClassifyError (err )
87+ errWithSource := backend .NewErrorWithSource (err , backend .ErrorSourceDownstream )
88+ return sqlutil .ErrorFrameFromQuery (query ), errWithSource
9489 }
9590
96- var errWithSource error
97- if errSource == backend .ErrorSourceDownstream {
98- errWithSource = backend .DownstreamError (fmt .Errorf ("%w: %s" , errType , err .Error ()))
99- } else {
100- errWithSource = backend .PluginError (fmt .Errorf ("%w: %s" , errType , err .Error ()))
91+ // Wrap with ErrorQuery to enable retry logic in datasource
92+ queryErr := fmt .Errorf ("%w: %w" , ErrorQuery , err )
93+
94+ // Handle driver specific errors
95+ if queryErrorMutator != nil {
96+ errWithSource = queryErrorMutator .MutateQueryError (queryErr )
97+ return sqlutil .ErrorFrameFromQuery (query ), errWithSource
10198 }
10299
103- q .metrics .CollectDuration (Source (errSource ), StatusError , time .Since (start ).Seconds ())
100+ // If we get to this point, assume the error is from the plugin
101+ errWithSource = backend .NewErrorWithSource (queryErr , backend .DefaultErrorSource )
102+
104103 return sqlutil .ErrorFrameFromQuery (query ), errWithSource
105104 }
106105 q .metrics .CollectDuration (SourceDownstream , StatusOK , time .Since (start ).Seconds ())
107106
108107 // Check for an error response
109108 if err := rows .Err (); err != nil {
109+ queryErr := fmt .Errorf ("%w: %w" , ErrorQuery , err )
110+ errWithSource := backend .NewErrorWithSource (queryErr , backend .DefaultErrorSource )
110111 if errors .Is (err , sql .ErrNoRows ) {
111112 // Should we even response with an error here?
112113 // The panel will simply show "no data"
113- errWithSource : = backend .DownstreamError (fmt .Errorf ("%s : %w " , "No results from query" , err ) )
114+ errWithSource = backend .NewErrorWithSource (fmt .Errorf ("%w : %s " , err , "Error response from database" ), backend . ErrorSourceDownstream )
114115 return sqlutil .ErrorFrameFromQuery (query ), errWithSource
115116 }
116-
117- errSource , _ := ClassifyError (err )
118- var errWithSource error
119- if errSource == backend .ErrorSourceDownstream {
120- errWithSource = backend .DownstreamError (fmt .Errorf ("%s: %w" , "Error response from database" , err ))
121- } else {
122- errWithSource = backend .PluginError (fmt .Errorf ("%s: %w" , "Error response from database" , err ))
117+ if queryErrorMutator != nil {
118+ errWithSource = queryErrorMutator .MutateQueryError (queryErr )
123119 }
124120
125- q .metrics .CollectDuration (Source (errSource ), StatusError , time .Since (start ).Seconds ())
121+ q .metrics .CollectDuration (Source (errWithSource . ErrorSource () ), StatusError , time .Since (start ).Seconds ())
126122 return sqlutil .ErrorFrameFromQuery (query ), errWithSource
127123 }
128124
@@ -132,23 +128,34 @@ func (q *DBQuery) Run(ctx context.Context, query *Query, args ...interface{}) (d
132128 }
133129 }()
134130
135- start = time .Now ()
136- // Convert the response to frames
131+ return q .convertRowsToFrames (rows , query , queryErrorMutator )
132+ }
133+
134+ func (q * DBQuery ) convertRowsToFrames (rows * sql.Rows , query * Query , queryErrorMutator QueryErrorMutator ) (data.Frames , error ) {
135+ source := SourcePlugin
136+ status := StatusOK
137+ start := time .Now ()
138+ defer func () {
139+ q .metrics .CollectDuration (source , status , time .Since (start ).Seconds ())
140+ }()
141+
137142 res , err := getFrames (rows , q .rowLimit , q .converters , q .fillMode , query )
138143 if err != nil {
139- errSource , _ := ClassifyError ( err )
144+ status = StatusError
140145
141146 // Additional checks for processing errors
142- if backend .IsDownstreamHTTPError (err ) || isProcessingDownstreamError (err ) {
143- errSource = backend .ErrorSourceDownstream
147+ if backend .IsDownstreamHTTPError (err ) {
148+ source = SourceDownstream
149+ } else if queryErrorMutator != nil {
150+ errWithSource := queryErrorMutator .MutateQueryError (err )
151+ source = Source (errWithSource .ErrorSource ())
144152 }
145153
146- errWithSource := backend .NewErrorWithSource (fmt .Errorf ("%w: %s" , err , "Could not process SQL results" ), errSource )
147- q .metrics .CollectDuration (Source (errSource ), StatusError , time .Since (start ).Seconds ())
148- return sqlutil .ErrorFrameFromQuery (query ), errWithSource
154+ return sqlutil .ErrorFrameFromQuery (query ), backend .NewErrorWithSource (
155+ fmt .Errorf ("%w: %s" , err , "Could not process SQL results" ),
156+ backend .ErrorSource (source ),
157+ )
149158 }
150-
151- q .metrics .CollectDuration (SourcePlugin , StatusOK , time .Since (start ).Seconds ())
152159 return res , nil
153160}
154161
@@ -306,26 +313,3 @@ func applyHeaders(query *Query, headers http.Header) *Query {
306313
307314 return query
308315}
309-
310- func isProcessingDownstreamError (err error ) bool {
311- downstreamErrors := []error {
312- data .ErrorInputFieldsWithoutRows ,
313- data .ErrorSeriesUnsorted ,
314- data .ErrorNullTimeValues ,
315- ErrorRowValidation ,
316- ErrorConnectionClosed ,
317- ErrorPGXLifecycle ,
318- }
319- for _ , e := range downstreamErrors {
320- if errors .Is (err , e ) {
321- return true
322- }
323- }
324-
325- // Check for PGX connection errors
326- if IsPGXConnectionError (err ) {
327- return true
328- }
329-
330- return false
331- }
0 commit comments