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 (
"context"
"errors"
"strings"
"time"
"github.com/VividCortex/mysqlerr"
"github.com/go-sql-driver/mysql"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"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/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) {
var folders []*folder.Folder
if ss.db.GetDBType() == migrator.MySQL {
return ss.getParentsMySQL(ctx, q)
}
recQuery := `
WITH RECURSIVE RecQry AS (
@ -195,14 +194,23 @@ func (ss *sqlStore) GetParents(ctx context.Context, q folder.GetParentsQuery) ([
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)
if err != nil {
return folder.ErrDatabaseError.Errorf("failed to get folder parents: %w", err)
}
return nil
})
return util.Reverse(folders[1:]), err
}); 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 util.Reverse(folders[1:]), nil
}
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
}
func (ss *sqlStore) getParentsMySQL(ctx context.Context, cmd folder.GetParentsQuery) ([]*folder.Folder, error) {
var foldrs []*folder.Folder
var foldr *folder.Folder
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
uid := cmd.UID
for uid != folder.GeneralFolderUID && len(foldrs) < 8 {
err := sess.Where("uid=? AND org_id=>", uid, cmd.OrgID).Find(foldr)
func (ss *sqlStore) getParentsMySQL(ctx context.Context, cmd folder.GetParentsQuery) (folders []*folder.Folder, err error) {
err = ss.db.WithDbSession(ctx, func(sess *db.Session) error {
uid := ""
ok, err := sess.SQL("SELECT parent_uid FROM folder WHERE org_id=? AND uid=?", cmd.OrgID, cmd.UID).Get(&uid)
if err != nil {
return err
}
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 {
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 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 ErrDatabaseError = errutil.NewBase(errutil.StatusInternal, "folder.database-error")
var ErrInternal = errutil.NewBase(errutil.StatusInternal, "folder.internal")
var ErrFolderTooDeep = errutil.NewBase(errutil.StatusInternal, "folder.too-deep")
const (
GeneralFolderUID = "general"