Skip to content

Commit b050949

Browse files
committed
use settings function in interface, let QueryContext handle context cancellation
1 parent 00d7688 commit b050949

File tree

5 files changed

+54
-42
lines changed

5 files changed

+54
-42
lines changed

datasource.go

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"fmt"
88
"net/http"
99
"sync"
10-
"time"
1110

1211
"github.com/grafana/grafana-plugin-sdk-go/backend"
1312
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
@@ -18,10 +17,11 @@ import (
1817
type sqldatasource struct {
1918
Completable
2019

21-
db *sql.DB
22-
c Driver
23-
settings backend.DataSourceInstanceSettings
24-
timeout time.Duration
20+
db *sql.DB
21+
c Driver
22+
23+
driverSettings DriverSettings
24+
settings backend.DataSourceInstanceSettings
2525

2626
backend.CallResourceHandler
2727
CustomRoutes map[string]func(http.ResponseWriter, *http.Request)
@@ -43,7 +43,7 @@ func (ds *sqldatasource) NewDatasource(settings backend.DataSourceInstanceSettin
4343
}
4444

4545
ds.CallResourceHandler = httpadapter.New(mux)
46-
ds.timeout = ds.c.Timeout(settings)
46+
ds.driverSettings = ds.c.Settings(settings)
4747

4848
return ds, nil
4949
}
@@ -103,13 +103,13 @@ func (ds *sqldatasource) handleQuery(ctx context.Context, req backend.DataQuery)
103103
}
104104

105105
// Apply the default FillMode, overwritting it if the query specifies it
106-
fillMode := ds.c.FillMode()
106+
fillMode := ds.driverSettings.FillMode
107107
if q.FillMissing != nil {
108108
fillMode = q.FillMissing
109109
}
110110

111-
if ds.timeout != 0 {
112-
tctx, cancel := context.WithTimeout(ctx, ds.timeout)
111+
if ds.driverSettings.Timeout != 0 {
112+
tctx, cancel := context.WithTimeout(ctx, ds.driverSettings.Timeout)
113113
defer cancel()
114114

115115
ctx = tctx
@@ -119,15 +119,13 @@ func (ds *sqldatasource) handleQuery(ctx context.Context, req backend.DataQuery)
119119
// * Some datasources (snowflake) expire connections or have an authentication token that expires if not used in 1 or 4 hours.
120120
// Because the datasource driver does not include an option for permanent connections, we retry the connection
121121
// if the query fails. NOTE: this does not include some errors like "ErrNoRows"
122-
123-
// This function will return an "ErrorTimeout" if the context's done channel receives data
124-
res, err := queryContext(ctx, ds.db, ds.c.Converters(), fillMode, q)
122+
res, err := query(ctx, ds.db, ds.c.Converters(), fillMode, q)
125123
if err == nil {
126124
return res, nil
127125
}
128126

129127
if errors.Is(err, ErrorNoResults) {
130-
return nil, nil
128+
return res, nil
131129
}
132130

133131
if errors.Is(err, ErrorQuery) {
@@ -136,7 +134,7 @@ func (ds *sqldatasource) handleQuery(ctx context.Context, req backend.DataQuery)
136134
return nil, err
137135
}
138136

139-
return queryContext(ctx, ds.db, ds.c.Converters(), fillMode, q)
137+
return query(ctx, ds.db, ds.c.Converters(), fillMode, q)
140138
}
141139

142140
return nil, err

driver.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@ import (
99
"github.com/grafana/grafana-plugin-sdk-go/data/sqlutil"
1010
)
1111

12+
type DriverSettings struct {
13+
Timeout time.Duration
14+
FillMode *data.FillMissing
15+
}
16+
1217
// Driver is a simple interface that defines how to connect to a backend SQL datasource
1318
// Plugin creators will need to implement this in order to create a managed datasource
1419
type Driver interface {
1520
// Connect connects to the database. It does not need to call `db.Ping()`
1621
Connect(backend.DataSourceInstanceSettings) (*sql.DB, error)
17-
Timeout(backend.DataSourceInstanceSettings) time.Duration
18-
FillMode() *data.FillMissing
22+
// Settings are read whenever the plugin is initialized, or after the data source settings are updated
23+
Settings(backend.DataSourceInstanceSettings) DriverSettings
1924
Macros() Macros
2025
Converters() []sqlutil.Converter
21-
// Timeout is used when sending a query to the backing data source. If the timeout is exceeded, the QueryData function will return an error.
2226
}

macros_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"time"
88

99
"github.com/grafana/grafana-plugin-sdk-go/backend"
10-
"github.com/grafana/grafana-plugin-sdk-go/data"
1110
"github.com/grafana/grafana-plugin-sdk-go/data/sqlutil"
1211
"github.com/stretchr/testify/assert"
1312
"github.com/stretchr/testify/require"
@@ -19,7 +18,7 @@ func (h *MockDB) Connect(backend.DataSourceInstanceSettings) (db *sql.DB, err er
1918
return
2019
}
2120

22-
func (h *MockDB) FillMode() (mode *data.FillMissing) {
21+
func (h *MockDB) Settings(backend.DataSourceInstanceSettings) (settings DriverSettings) {
2322
return
2423
}
2524

query.go

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -91,28 +91,6 @@ func getErrorFrameFromQuery(query *Query) data.Frames {
9191
return frames
9292
}
9393

94-
func queryContext(ctx context.Context, db *sql.DB, converters []sqlutil.Converter, fillMode *data.FillMissing, q *Query) (data.Frames, error) {
95-
var (
96-
done = make(chan bool)
97-
res = data.Frames{}
98-
err error
99-
)
100-
101-
go func() {
102-
res, err = query(ctx, db, converters, fillMode, q)
103-
done <- true
104-
}()
105-
106-
select {
107-
case <-done:
108-
// The done channel will receive data if the query funciton has completed
109-
return res, err
110-
case <-ctx.Done():
111-
// The context's done channel will receive data if the timeout is exceeded
112-
return nil, ErrorTimeout
113-
}
114-
}
115-
11694
// query sends the query to the sql.DB and converts the rows to a dataframe.
11795
func query(ctx context.Context, db *sql.DB, converters []sqlutil.Converter, fillMode *data.FillMissing, query *Query) (data.Frames, error) {
11896
// Query the rows from the database
@@ -140,7 +118,7 @@ func query(ctx context.Context, db *sql.DB, converters []sqlutil.Converter, fill
140118
// Convert the response to frames
141119
res, err := getFrames(rows, -1, converters, fillMode, query)
142120
if err != nil {
143-
return nil, fmt.Errorf("%w: %s", err, "Could not process SQL results")
121+
return getErrorFrameFromQuery(query), fmt.Errorf("%w: %s", err, "Could not process SQL results")
144122
}
145123

146124
return res, nil

query_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package sqlds
2+
3+
import (
4+
"database/sql"
5+
"time"
6+
7+
"github.com/grafana/grafana-plugin-sdk-go/backend"
8+
"github.com/grafana/grafana-plugin-sdk-go/data"
9+
"github.com/grafana/grafana-plugin-sdk-go/data/sqlutil"
10+
)
11+
12+
type MockTimeoutDriver struct{}
13+
14+
// Connect connects to the database. It does not need to call `db.Ping()`
15+
func (m *MockTimeoutDriver) Connect(_ backend.DataSourceInstanceSettings) (*sql.DB, error) {
16+
return nil, nil
17+
}
18+
19+
func (m *MockTimeoutDriver) FillMode() *data.FillMissing {
20+
return nil
21+
}
22+
23+
func (m *MockTimeoutDriver) Macros() Macros {
24+
return nil
25+
}
26+
27+
func (m *MockTimeoutDriver) Converters() []sqlutil.Converter {
28+
return nil
29+
}
30+
31+
func (m *MockTimeoutDriver) Timeout(backend.DataSourceInstanceSettings) time.Duration {
32+
return time.Second
33+
}

0 commit comments

Comments
 (0)