Annotations: Use dashboard uids instead of dashboard ids (#106676)

This commit is contained in:
Stephanie Hingtgen
2025-06-13 13:59:24 -05:00
committed by GitHub
parent 47f3073ab8
commit a8886ad5ec
20 changed files with 410 additions and 228 deletions
@@ -5,11 +5,11 @@ import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"time"
"github.com/grafana/grafana/pkg/services/annotations/accesscontrol"
"github.com/grafana/grafana/pkg/services/sqlstore/migrations"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/apimachinery/identity"
@@ -46,6 +46,13 @@ type xormRepositoryImpl struct {
}
func NewXormStore(cfg *setting.Cfg, l log.Logger, db db.DB, tagService tag.Service) *xormRepositoryImpl {
// populate dashboard_uid at startup, to ensure safe downgrades & upgrades after
// the initial migration occurs
err := migrations.RunDashboardUIDMigrations(db.GetEngine().NewSession(), db.GetEngine().DriverName())
if err != nil {
l.Error("failed to populate dashboard_uid for annotations", "error", err)
}
return &xormRepositoryImpl{
cfg: cfg,
db: db,
@@ -255,6 +262,7 @@ func (r *xormRepositoryImpl) Get(ctx context.Context, query annotations.ItemQuer
annotation.id,
annotation.epoch as time,
annotation.epoch_end as time_end,
annotation.dashboard_uid,
annotation.dashboard_id,
annotation.panel_id,
annotation.new_state,
@@ -292,11 +300,18 @@ func (r *xormRepositoryImpl) Get(ctx context.Context, query annotations.ItemQuer
params = append(params, query.AlertUID, query.OrgID)
}
// nolint: staticcheck
if query.DashboardID != 0 {
sql.WriteString(` AND a.dashboard_id = ?`)
params = append(params, query.DashboardID)
}
// note: orgID is already required above
if query.DashboardUID != "" {
sql.WriteString(` AND a.dashboard_uid = ?`)
params = append(params, query.DashboardUID)
}
if query.PanelID != 0 {
sql.WriteString(` AND a.panel_id = ?`)
params = append(params, query.PanelID)
@@ -351,13 +366,11 @@ func (r *xormRepositoryImpl) Get(ctx context.Context, query annotations.ItemQuer
}
}
acFilter, err := r.getAccessControlFilter(query.SignedInUser, accessResources)
if err != nil {
return err
}
acFilter, acParams := r.getAccessControlFilter(query.SignedInUser, accessResources)
if acFilter != "" {
sql.WriteString(fmt.Sprintf(" AND (%s)", acFilter))
}
params = append(params, acParams...)
// order of ORDER BY arguments match the order of a sql index for performance
orderBy := " ORDER BY a.org_id, a.epoch_end DESC, a.epoch DESC"
@@ -377,41 +390,30 @@ func (r *xormRepositoryImpl) Get(ctx context.Context, query annotations.ItemQuer
return items, err
}
func (r *xormRepositoryImpl) getAccessControlFilter(user identity.Requester, accessResources *accesscontrol.AccessResources) (string, error) {
func (r *xormRepositoryImpl) getAccessControlFilter(user identity.Requester, accessResources *accesscontrol.AccessResources) (string, []any) {
if accessResources.SkipAccessControlFilter {
return "", nil
}
var filters []string
var params []any
if accessResources.CanAccessOrgAnnotations {
filters = append(filters, "a.dashboard_id = 0")
}
if accessResources.CanAccessDashAnnotations {
var dashboardIDs []int64
for _, id := range accessResources.Dashboards {
dashboardIDs = append(dashboardIDs, id)
}
var inClause string
if len(dashboardIDs) == 0 {
inClause = "SELECT * FROM (SELECT 0 LIMIT 0) tt" // empty set
if len(accessResources.Dashboards) == 0 {
filters = append(filters, "1=0") // empty set
} else {
b := make([]byte, 0, 3*len(dashboardIDs))
b = strconv.AppendInt(b, dashboardIDs[0], 10)
for _, num := range dashboardIDs[1:] {
b = append(b, ',')
b = strconv.AppendInt(b, num, 10)
filters = append(filters, fmt.Sprintf("a.dashboard_uid IN (%s)", strings.Repeat("?,", len(accessResources.Dashboards)-1)+"?"))
for uid := range accessResources.Dashboards {
params = append(params, uid)
}
inClause = string(b)
}
filters = append(filters, fmt.Sprintf("a.dashboard_id IN (%s)", inClause))
}
return strings.Join(filters, " OR "), nil
return strings.Join(filters, " OR "), params
}
func (r *xormRepositoryImpl) Delete(ctx context.Context, params *annotations.DeleteParams) error {
@@ -433,14 +435,27 @@ func (r *xormRepositoryImpl) Delete(ctx context.Context, params *annotations.Del
if _, err := sess.Exec(sql, params.ID, params.OrgID); err != nil {
return err
}
} else if params.DashboardUID != "" {
annoTagSQL = "DELETE FROM annotation_tag WHERE annotation_id IN (SELECT id FROM annotation WHERE dashboard_uid = ? AND panel_id = ? AND org_id = ?)"
sql = "DELETE FROM annotation WHERE dashboard_uid = ? AND panel_id = ? AND org_id = ?"
if _, err := sess.Exec(annoTagSQL, params.DashboardUID, params.PanelID, params.OrgID); err != nil {
return err
}
if _, err := sess.Exec(sql, params.DashboardUID, params.PanelID, params.OrgID); err != nil {
return err
}
} else {
annoTagSQL = "DELETE FROM annotation_tag WHERE annotation_id IN (SELECT id FROM annotation WHERE dashboard_id = ? AND panel_id = ? AND org_id = ?)"
sql = "DELETE FROM annotation WHERE dashboard_id = ? AND panel_id = ? AND org_id = ?"
// nolint: staticcheck
if _, err := sess.Exec(annoTagSQL, params.DashboardID, params.PanelID, params.OrgID); err != nil {
return err
}
// nolint: staticcheck
if _, err := sess.Exec(sql, params.DashboardID, params.PanelID, params.OrgID); err != nil {
return err
}