Nested Folders: Use recursive query if the driver supports it (#58178)

* Nested Folders: Try first recursive query and fallback if it's not supported

* Apply suggestion from code review

Fix error msgID
This commit is contained in:
Sofia Papagiannaki 2022-11-08 21:53:05 +02:00 committed by GitHub
parent ed64133943
commit 4d2be7a277
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 18 deletions

View File

@ -2,14 +2,16 @@ 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/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
@ -183,9 +185,6 @@ func (ss *sqlStore) Get(ctx context.Context, q folder.GetFolderQuery) (*folder.F
func (ss *sqlStore) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) { func (ss *sqlStore) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
var folders []*folder.Folder var folders []*folder.Folder
if ss.db.GetDBType() == migrator.MySQL {
return ss.getParentsMySQL(ctx, q)
}
recQuery := ` recQuery := `
WITH RECURSIVE RecQry AS ( WITH RECURSIVE RecQry AS (
@ -195,14 +194,23 @@ func (ss *sqlStore) GetParents(ctx context.Context, q folder.GetParentsQuery) ([
SELECT * FROM RecQry; SELECT * FROM RecQry;
` `
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error { if err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
err := sess.SQL(recQuery, q.UID, q.OrgID).Find(&folders) 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 folder.ErrDatabaseError.Errorf("failed to get folder parents: %w", err)
} }
return nil return nil
}) }); err != nil {
return util.Reverse(folders[1:]), err 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 util.Reverse(folders[1:]), nil
} }
func (ss *sqlStore) GetChildren(ctx context.Context, q folder.GetTreeQuery) ([]*folder.Folder, error) { func (ss *sqlStore) GetChildren(ctx context.Context, q folder.GetTreeQuery) ([]*folder.Folder, error) {
@ -228,20 +236,32 @@ func (ss *sqlStore) GetChildren(ctx context.Context, q folder.GetTreeQuery) ([]*
return folders, err return folders, err
} }
func (ss *sqlStore) getParentsMySQL(ctx context.Context, cmd folder.GetParentsQuery) ([]*folder.Folder, error) { func (ss *sqlStore) getParentsMySQL(ctx context.Context, cmd folder.GetParentsQuery) (folders []*folder.Folder, err error) {
var foldrs []*folder.Folder err = ss.db.WithDbSession(ctx, func(sess *db.Session) error {
var foldr *folder.Folder uid := ""
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error { ok, err := sess.SQL("SELECT parent_uid FROM folder WHERE org_id=? AND uid=?", cmd.OrgID, cmd.UID).Get(&uid)
uid := cmd.UID if err != nil {
for uid != folder.GeneralFolderUID && len(foldrs) < 8 { return err
err := sess.Where("uid=? AND org_id=>", uid, cmd.OrgID).Find(foldr) }
if !ok {
return folder.ErrFolderNotFound
}
for {
f := &folder.Folder{}
ok, err := sess.SQL("SELECT * FROM folder WHERE org_id=? AND uid=?", cmd.OrgID, uid).Get(f)
if err != nil { if err != nil {
return folder.ErrDatabaseError.Errorf("failed to get folder parents: %w", err) return err
}
if !ok {
break
}
folders = append(folders, f)
uid = f.ParentUID
if len(folders) > folder.MaxNestedFolderDepth {
return folder.ErrFolderTooDeep
} }
foldrs = append(foldrs, foldr)
uid = foldr.ParentUID
} }
return nil return nil
}) })
return foldrs, err return folders, err
} }

View File

@ -10,6 +10,7 @@ var ErrMaximumDepthReached = errutil.NewBase(errutil.StatusBadRequest, "folder.m
var ErrBadRequest = errutil.NewBase(errutil.StatusBadRequest, "folder.bad-request") var ErrBadRequest = errutil.NewBase(errutil.StatusBadRequest, "folder.bad-request")
var ErrDatabaseError = errutil.NewBase(errutil.StatusInternal, "folder.database-error") var ErrDatabaseError = errutil.NewBase(errutil.StatusInternal, "folder.database-error")
var ErrInternal = errutil.NewBase(errutil.StatusInternal, "folder.internal") var ErrInternal = errutil.NewBase(errutil.StatusInternal, "folder.internal")
var ErrFolderTooDeep = errutil.NewBase(errutil.StatusInternal, "folder.too-deep")
const ( const (
GeneralFolderUID = "general" GeneralFolderUID = "general"