mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Add function for detecting if the SQL driver supported CTEs (#64441)
* Add interface method for detecting if the SQL driver supported CTEs * Update nested folder store to call RecursiveQueriesAreSupported()
This commit is contained in:
parent
fd6e97d52d
commit
4ee0be6fdf
@ -20,6 +20,9 @@ type DB interface {
|
|||||||
GetSqlxSession() *session.SessionDB
|
GetSqlxSession() *session.SessionDB
|
||||||
InTransaction(ctx context.Context, fn func(ctx context.Context) error) error
|
InTransaction(ctx context.Context, fn func(ctx context.Context) error) error
|
||||||
Quote(value string) string
|
Quote(value string) string
|
||||||
|
// RecursiveQueriesAreSupported runs a dummy recursive query and it returns true
|
||||||
|
// if the query runs successfully or false if it fails with mysqlerr.ER_PARSE_ERROR error or any other error
|
||||||
|
RecursiveQueriesAreSupported() (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Session = sqlstore.DBSession
|
type Session = sqlstore.DBSession
|
||||||
|
@ -51,6 +51,10 @@ func (f *FakeDB) Quote(value string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FakeDB) RecursiveQueriesAreSupported() (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: service-specific methods not yet split out ; to be removed
|
// TODO: service-specific methods not yet split out ; to be removed
|
||||||
func (f *FakeDB) UpdateTempUserWithEmailSent(ctx context.Context, cmd *tempuser.UpdateTempUserWithEmailSentCommand) error {
|
func (f *FakeDB) UpdateTempUserWithEmailSent(ctx context.Context, cmd *tempuser.UpdateTempUserWithEmailSentCommand) error {
|
||||||
return f.ExpectedError
|
return f.ExpectedError
|
||||||
|
@ -2,13 +2,9 @@ package folderimpl
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/VividCortex/mysqlerr"
|
|
||||||
"github.com/go-sql-driver/mysql"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/slugify"
|
"github.com/grafana/grafana/pkg/infra/slugify"
|
||||||
@ -196,22 +192,25 @@ func (ss *sqlStore) GetParents(ctx context.Context, q folder.GetParentsQuery) ([
|
|||||||
SELECT * FROM RecQry;
|
SELECT * FROM RecQry;
|
||||||
`
|
`
|
||||||
|
|
||||||
if err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
recursiveQueriesAreSupported, err := ss.db.RecursiveQueriesAreSupported()
|
||||||
err := sess.SQL(recQuery, q.UID, q.OrgID).Find(&folders)
|
if err != nil {
|
||||||
if err != nil {
|
|
||||||
return folder.ErrDatabaseError.Errorf("failed to get folder parents: %w", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
var driverErr *mysql.MySQLError
|
|
||||||
if errors.As(err, &driverErr) {
|
|
||||||
if driverErr.Number == mysqlerr.ER_PARSE_ERROR {
|
|
||||||
ss.log.Debug("recursive CTE subquery is not supported; it fallbacks to the iterative implementation")
|
|
||||||
return ss.getParentsMySQL(ctx, q)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
switch recursiveQueriesAreSupported {
|
||||||
|
case true:
|
||||||
|
if err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
||||||
|
err := sess.SQL(recQuery, q.UID, q.OrgID).Find(&folders)
|
||||||
|
if err != nil {
|
||||||
|
return folder.ErrDatabaseError.Errorf("failed to get folder parents: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ss.log.Debug("recursive CTE subquery is not supported; it fallbacks to the iterative implementation")
|
||||||
|
return ss.getParentsMySQL(ctx, q)
|
||||||
|
}
|
||||||
|
|
||||||
if len(folders) < 1 {
|
if len(folders) < 1 {
|
||||||
// the query is expected to return at least the same folder
|
// the query is expected to return at least the same folder
|
||||||
|
@ -2,6 +2,7 @@ package sqlstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@ -11,6 +12,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/VividCortex/mysqlerr"
|
||||||
"github.com/dlmiddlecote/sqlstats"
|
"github.com/dlmiddlecote/sqlstats"
|
||||||
"github.com/go-sql-driver/mysql"
|
"github.com/go-sql-driver/mysql"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
@ -44,14 +46,15 @@ type SQLStore struct {
|
|||||||
sqlxsession *session.SessionDB
|
sqlxsession *session.SessionDB
|
||||||
CacheService *localcache.CacheService
|
CacheService *localcache.CacheService
|
||||||
|
|
||||||
bus bus.Bus
|
bus bus.Bus
|
||||||
dbCfg DatabaseConfig
|
dbCfg DatabaseConfig
|
||||||
engine *xorm.Engine
|
engine *xorm.Engine
|
||||||
log log.Logger
|
log log.Logger
|
||||||
Dialect migrator.Dialect
|
Dialect migrator.Dialect
|
||||||
skipEnsureDefaultOrgAndUser bool
|
skipEnsureDefaultOrgAndUser bool
|
||||||
migrations registry.DatabaseMigrator
|
migrations registry.DatabaseMigrator
|
||||||
tracer tracing.Tracer
|
tracer tracing.Tracer
|
||||||
|
recursiveQueriesAreSupported *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, cacheService *localcache.CacheService, migrations registry.DatabaseMigrator, bus bus.Bus, tracer tracing.Tracer) (*SQLStore, error) {
|
func ProvideService(cfg *setting.Cfg, cacheService *localcache.CacheService, migrations registry.DatabaseMigrator, bus bus.Bus, tracer tracing.Tracer) (*SQLStore, error) {
|
||||||
@ -476,6 +479,43 @@ func (ss *SQLStore) readConfig() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ss *SQLStore) RecursiveQueriesAreSupported() (bool, error) {
|
||||||
|
if ss.recursiveQueriesAreSupported != nil {
|
||||||
|
return *ss.recursiveQueriesAreSupported, nil
|
||||||
|
}
|
||||||
|
recursiveQueriesAreSupported := func() (bool, error) {
|
||||||
|
var result []int
|
||||||
|
if err := ss.WithDbSession(context.Background(), func(sess *DBSession) error {
|
||||||
|
recQry := `WITH RECURSIVE cte (n) AS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
UNION ALL
|
||||||
|
SELECT n + 1 FROM cte WHERE n < 2
|
||||||
|
)
|
||||||
|
SELECT * FROM cte;
|
||||||
|
`
|
||||||
|
err := sess.SQL(recQry).Find(&result)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
var driverErr *mysql.MySQLError
|
||||||
|
if errors.As(err, &driverErr) {
|
||||||
|
if driverErr.Number == mysqlerr.ER_PARSE_ERROR {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
areSupported, err := recursiveQueriesAreSupported()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
ss.recursiveQueriesAreSupported = &areSupported
|
||||||
|
return *ss.recursiveQueriesAreSupported, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ITestDB is an interface of arguments for testing db
|
// ITestDB is an interface of arguments for testing db
|
||||||
type ITestDB interface {
|
type ITestDB interface {
|
||||||
Helper()
|
Helper()
|
||||||
|
Loading…
Reference in New Issue
Block a user