mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 08:05:43 -06:00
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:
parent
ed64133943
commit
4d2be7a277
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user