2022-09-19 02:54:37 -05:00
package annotationsimpl
2016-08-01 03:07:00 -05:00
import (
2016-08-30 02:32:56 -05:00
"bytes"
2022-03-22 06:20:57 -05:00
"context"
2017-10-07 03:31:39 -05:00
"errors"
2016-08-30 02:32:56 -05:00
"fmt"
2023-11-14 17:11:01 -06:00
"strconv"
2016-09-14 01:36:44 -05:00
"strings"
2018-03-21 20:22:58 -05:00
"time"
2016-08-30 02:32:56 -05:00
2023-11-14 17:11:01 -06:00
"github.com/grafana/grafana/pkg/services/annotations/accesscontrol"
2022-10-19 08:02:15 -05:00
"github.com/grafana/grafana/pkg/infra/db"
2022-09-19 02:54:37 -05:00
"github.com/grafana/grafana/pkg/infra/log"
2016-08-01 03:07:00 -05:00
"github.com/grafana/grafana/pkg/services/annotations"
2023-10-06 04:59:48 -05:00
"github.com/grafana/grafana/pkg/services/auth/identity"
2022-11-04 10:39:26 -05:00
"github.com/grafana/grafana/pkg/services/sqlstore"
2022-09-21 07:04:01 -05:00
"github.com/grafana/grafana/pkg/services/tag"
2022-09-19 02:54:37 -05:00
"github.com/grafana/grafana/pkg/setting"
2016-08-01 03:07:00 -05:00
)
2019-08-16 03:49:30 -05:00
// Update the item so that EpochEnd >= Epoch
func validateTimeRange ( item * annotations . Item ) error {
if item . EpochEnd == 0 {
if item . Epoch == 0 {
2021-03-29 08:47:16 -05:00
return annotations . ErrTimerangeMissing
2019-08-16 03:49:30 -05:00
}
item . EpochEnd = item . Epoch
}
if item . Epoch == 0 {
item . Epoch = item . EpochEnd
}
if item . EpochEnd < item . Epoch {
2020-09-22 09:22:19 -05:00
item . Epoch , item . EpochEnd = item . EpochEnd , item . Epoch
2019-08-16 03:49:30 -05:00
}
return nil
}
2022-09-22 07:27:48 -05:00
type xormRepositoryImpl struct {
2023-11-14 17:11:01 -06:00
cfg * setting . Cfg
db db . DB
log log . Logger
tagService tag . Service
}
func NewXormStore ( cfg * setting . Cfg , l log . Logger , db db . DB , tagService tag . Service ) * xormRepositoryImpl {
return & xormRepositoryImpl {
cfg : cfg ,
db : db ,
log : l . New ( "store" , "xorm" ) ,
tagService : tagService ,
}
2022-04-11 07:18:38 -05:00
}
2022-09-22 07:27:48 -05:00
func ( r * xormRepositoryImpl ) Add ( ctx context . Context , item * annotations . Item ) error {
2022-09-21 07:04:01 -05:00
tags := tag . ParseTagPairs ( item . Tags )
item . Tags = tag . JoinTagPairs ( tags )
item . Created = timeNow ( ) . UnixNano ( ) / int64 ( time . Millisecond )
item . Updated = item . Created
if item . Epoch == 0 {
item . Epoch = item . Created
}
2022-09-23 05:04:41 -05:00
if err := r . validateItem ( item ) ; err != nil {
2022-09-21 07:04:01 -05:00
return err
}
2018-03-22 10:21:47 -05:00
2022-10-19 08:02:15 -05:00
return r . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
2016-08-01 03:07:00 -05:00
if _ , err := sess . Table ( "annotation" ) . Insert ( item ) ; err != nil {
return err
}
2023-07-31 10:19:59 -05:00
return r . ensureTags ( ctx , item . ID , item . Tags )
2022-11-04 10:39:26 -05:00
} )
}
// AddMany inserts large batches of annotations at once.
// It does not return IDs associated with created annotations, and it does not support annotations with tags. If you need this functionality, use the single-item Add instead.
// This is due to a limitation with some supported databases:
// We cannot correlate the IDs of batch-inserted records without acquiring a full table lock in MySQL.
// Annotations have no other uniquifier field, so we also cannot re-query for them after the fact.
// So, callers can only reliably use this endpoint if they don't care about returned IDs.
func ( r * xormRepositoryImpl ) AddMany ( ctx context . Context , items [ ] annotations . Item ) error {
hasTags := make ( [ ] annotations . Item , 0 )
hasNoTags := make ( [ ] annotations . Item , 0 )
2023-01-06 20:23:46 -06:00
if len ( items ) == 0 {
return nil
}
2023-01-04 10:16:54 -06:00
for i := range items {
// The validation logic needs to work in terms of pointers.
// So, force everything else to work in terms of pointers too, to avoid any implicit extra copying.
item := & items [ i ]
2022-11-04 10:39:26 -05:00
tags := tag . ParseTagPairs ( item . Tags )
item . Tags = tag . JoinTagPairs ( tags )
item . Created = timeNow ( ) . UnixNano ( ) / int64 ( time . Millisecond )
item . Updated = item . Created
if item . Epoch == 0 {
item . Epoch = item . Created
}
2023-01-04 10:16:54 -06:00
if err := r . validateItem ( item ) ; err != nil {
2022-11-04 10:39:26 -05:00
return err
}
if len ( item . Tags ) > 0 {
2023-01-04 10:16:54 -06:00
hasTags = append ( hasTags , * item )
2022-11-04 10:39:26 -05:00
} else {
2023-01-04 10:16:54 -06:00
hasNoTags = append ( hasNoTags , * item )
2022-11-04 10:39:26 -05:00
}
}
return r . db . WithDbSession ( ctx , func ( sess * sqlstore . DBSession ) error {
// We can batch-insert every annotation with no tags. If an annotation has tags, we need the ID.
opts := sqlstore . NativeSettingsForDialect ( r . db . GetDialect ( ) )
if _ , err := sess . BulkInsert ( "annotation" , hasNoTags , opts ) ; err != nil {
return err
}
for i , item := range hasTags {
if _ , err := sess . Table ( "annotation" ) . Insert ( item ) ; err != nil {
return err
}
2023-07-31 10:19:59 -05:00
itemWithID := & hasTags [ i ]
if err := r . ensureTags ( ctx , itemWithID . ID , itemWithID . Tags ) ; err != nil {
2022-11-04 10:39:26 -05:00
return err
}
}
return nil
} )
}
2016-08-01 03:07:00 -05:00
2022-09-22 07:27:48 -05:00
func ( r * xormRepositoryImpl ) Update ( ctx context . Context , item * annotations . Item ) error {
2023-07-07 08:21:49 -05:00
return r . db . InTransaction ( ctx , func ( ctx context . Context ) error {
return r . update ( ctx , item )
} )
}
func ( r * xormRepositoryImpl ) update ( ctx context . Context , item * annotations . Item ) error {
return r . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
2017-10-07 03:31:39 -05:00
var (
isExist bool
err error
)
existing := new ( annotations . Item )
2023-02-03 10:23:09 -06:00
isExist , err = sess . Table ( "annotation" ) . Where ( "id=? AND org_id=?" , item . ID , item . OrgID ) . Get ( existing )
2017-10-07 03:31:39 -05:00
if err != nil {
return err
}
if ! isExist {
2020-11-05 04:57:20 -06:00
return errors . New ( "annotation not found" )
2017-10-07 03:31:39 -05:00
}
2020-10-26 01:45:30 -05:00
existing . Updated = timeNow ( ) . UnixNano ( ) / int64 ( time . Millisecond )
2017-10-07 03:31:39 -05:00
existing . Text = item . Text
2019-08-16 03:49:30 -05:00
if item . Epoch != 0 {
existing . Epoch = item . Epoch
}
if item . EpochEnd != 0 {
existing . EpochEnd = item . EpochEnd
}
2022-12-26 08:53:52 -06:00
if item . Data != nil {
existing . Data = item . Data
}
2017-10-07 03:31:39 -05:00
if item . Tags != nil {
2023-07-31 10:19:59 -05:00
err := r . ensureTags ( ctx , existing . ID , item . Tags )
Outdent code after if block that ends with return (golint)
This commit fixes the following golint warnings:
pkg/bus/bus.go:64:9: if block ends with a return statement, so drop this else and outdent its block
pkg/bus/bus.go:84:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:137:10: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:177:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:183:10: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:199:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:208:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/components/dynmap/dynmap.go:236:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:242:10: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:257:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:263:10: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:278:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:284:10: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:299:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:331:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:350:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:356:10: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:366:12: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:390:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:396:10: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:405:12: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:427:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:433:10: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:442:12: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:459:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:465:10: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:474:12: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:491:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:497:10: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:506:12: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:523:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:529:10: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:538:12: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:555:9: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:561:10: if block ends with a return statement, so drop this else and outdent its block
pkg/components/dynmap/dynmap.go:570:12: if block ends with a return statement, so drop this else and outdent its block
pkg/login/ldap.go:55:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/login/ldap_test.go:372:10: if block ends with a return statement, so drop this else and outdent its block
pkg/middleware/middleware_test.go:213:12: if block ends with a return statement, so drop this else and outdent its block
pkg/plugins/dashboard_importer.go:153:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/plugins/dashboards_updater.go:39:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/plugins/dashboards_updater.go:121:10: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/plugins/plugins.go:210:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/plugins/plugins.go:235:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/services/alerting/eval_context.go:111:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/services/alerting/notifier.go:92:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/services/alerting/notifier.go:98:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/services/alerting/notifier.go:122:10: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/services/alerting/rule.go:108:10: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/services/alerting/rule.go:118:10: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/services/alerting/rule.go:121:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/services/alerting/notifiers/telegram.go:94:10: if block ends with a return statement, so drop this else and outdent its block
pkg/services/sqlstore/annotation.go:34:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/services/sqlstore/annotation.go:99:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/services/sqlstore/dashboard_test.go:107:13: if block ends with a return statement, so drop this else and outdent its block
pkg/services/sqlstore/plugin_setting.go:78:10: if block ends with a return statement, so drop this else and outdent its block
pkg/services/sqlstore/preferences.go:91:10: if block ends with a return statement, so drop this else and outdent its block
pkg/services/sqlstore/user.go:50:10: if block ends with a return statement, so drop this else and outdent its block
pkg/services/sqlstore/migrator/migrator.go:106:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/services/sqlstore/migrator/postgres_dialect.go:48:10: if block ends with a return statement, so drop this else and outdent its block
pkg/tsdb/time_range.go:59:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/tsdb/time_range.go:67:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
pkg/tsdb/cloudwatch/metric_find_query.go:225:9: if block ends with a return statement, so drop this else and outdent its block
pkg/util/filepath.go:68:11: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
2018-04-27 15:42:49 -05:00
if err != nil {
return err
}
2017-10-07 03:31:39 -05:00
}
existing . Tags = item . Tags
2017-04-12 09:26:34 -05:00
2022-09-23 05:04:41 -05:00
if err := r . validateItem ( existing ) ; err != nil {
return err
}
2023-02-03 10:23:09 -06:00
_ , err = sess . Table ( "annotation" ) . ID ( existing . ID ) . Cols ( "epoch" , "text" , "epoch_end" , "updated" , "tags" , "data" ) . Update ( existing )
2018-04-16 12:54:23 -05:00
return err
2017-04-12 09:26:34 -05:00
} )
}
2023-07-31 10:19:59 -05:00
func ( r * xormRepositoryImpl ) ensureTags ( ctx context . Context , annotationID int64 , tags [ ] string ) error {
return r . db . WithDbSession ( ctx , func ( sess * sqlstore . DBSession ) error {
var tagsInsert [ ] annotationTag
var tagsDelete [ ] int64
expectedTags , err := r . tagService . EnsureTagsExist ( ctx , tag . ParseTagPairs ( tags ) )
if err != nil {
return err
}
expected := tagSet ( func ( t * tag . Tag ) int64 {
return t . Id
} , expectedTags )
existingTags := make ( [ ] annotationTag , 0 )
if err := sess . SQL ( "SELECT annotation_id, tag_id FROM annotation_tag WHERE annotation_id = ?" , annotationID ) . Find ( & existingTags ) ; err != nil {
return err
}
existing := tagSet ( func ( t annotationTag ) int64 {
return t . TagID
} , existingTags )
for t := range expected {
if _ , exists := existing [ t ] ; ! exists {
tagsInsert = append ( tagsInsert , annotationTag {
AnnotationID : annotationID ,
TagID : t ,
} )
}
}
for t := range existing {
if _ , exists := expected [ t ] ; ! exists {
tagsDelete = append ( tagsDelete , t )
}
}
if len ( tagsDelete ) != 0 {
if _ , err := sess . MustCols ( "annotation_id" , "tag_id" ) . In ( "tag_id" , tagsDelete ) . Delete ( annotationTag { AnnotationID : annotationID } ) ; err != nil {
return err
}
}
if len ( tagsInsert ) != 0 {
if _ , err := sess . InsertMulti ( tagsInsert ) ; err != nil {
return err
}
}
return nil
} )
}
func tagSet [ T any ] ( fn func ( T ) int64 , list [ ] T ) map [ int64 ] struct { } {
set := make ( map [ int64 ] struct { } , len ( list ) )
for _ , item := range list {
set [ fn ( item ) ] = struct { } { }
}
return set
}
2023-11-14 17:11:01 -06:00
func ( r * xormRepositoryImpl ) Get ( ctx context . Context , query * annotations . ItemQuery , accessResources * accesscontrol . AccessResources ) ( [ ] * annotations . ItemDTO , error ) {
2016-08-30 02:32:56 -05:00
var sql bytes . Buffer
params := make ( [ ] interface { } , 0 )
2022-03-22 06:20:57 -05:00
items := make ( [ ] * annotations . ItemDTO , 0 )
2022-10-19 08:02:15 -05:00
err := r . db . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-03-22 06:20:57 -05:00
sql . WriteString ( `
SELECT
annotation . id ,
annotation . epoch as time ,
annotation . epoch_end as time_end ,
annotation . dashboard_id ,
annotation . panel_id ,
annotation . new_state ,
annotation . prev_state ,
annotation . alert_id ,
annotation . text ,
annotation . tags ,
annotation . data ,
annotation . created ,
annotation . updated ,
usr . email ,
usr . login ,
alert . name as alert_name
FROM annotation
2022-09-19 02:54:37 -05:00
LEFT OUTER JOIN ` + r.db.GetDialect().Quote("user") + ` as usr on usr . id = annotation . user_id
2022-03-22 06:20:57 -05:00
LEFT OUTER JOIN alert on alert . id = annotation . alert_id
INNER JOIN (
SELECT a . id from annotation a
` )
sql . WriteString ( ` WHERE a.org_id = ? ` )
2023-02-03 10:23:09 -06:00
params = append ( params , query . OrgID )
2022-03-22 06:20:57 -05:00
2023-02-03 10:23:09 -06:00
if query . AnnotationID != 0 {
2022-03-22 06:20:57 -05:00
// fmt.Print("annotation query")
sql . WriteString ( ` AND a.id = ? ` )
2023-02-03 10:23:09 -06:00
params = append ( params , query . AnnotationID )
2022-03-22 06:20:57 -05:00
}
2016-08-30 02:32:56 -05:00
2023-02-03 10:23:09 -06:00
if query . AlertID != 0 {
2022-03-22 06:20:57 -05:00
sql . WriteString ( ` AND a.alert_id = ? ` )
2023-02-03 10:23:09 -06:00
params = append ( params , query . AlertID )
2022-03-22 06:20:57 -05:00
}
2017-12-20 17:52:21 -06:00
2023-02-03 10:23:09 -06:00
if query . DashboardID != 0 {
2022-03-22 06:20:57 -05:00
sql . WriteString ( ` AND a.dashboard_id = ? ` )
2023-02-03 10:23:09 -06:00
params = append ( params , query . DashboardID )
2022-03-22 06:20:57 -05:00
}
2016-09-09 04:30:55 -05:00
2023-02-03 10:23:09 -06:00
if query . PanelID != 0 {
2022-03-22 06:20:57 -05:00
sql . WriteString ( ` AND a.panel_id = ? ` )
2023-02-03 10:23:09 -06:00
params = append ( params , query . PanelID )
2022-03-22 06:20:57 -05:00
}
2016-09-09 04:30:55 -05:00
2023-02-03 10:23:09 -06:00
if query . UserID != 0 {
2022-03-22 06:20:57 -05:00
sql . WriteString ( ` AND a.user_id = ? ` )
2023-02-03 10:23:09 -06:00
params = append ( params , query . UserID )
2022-03-22 06:20:57 -05:00
}
2016-09-09 04:30:55 -05:00
2022-03-22 06:20:57 -05:00
if query . From > 0 && query . To > 0 {
sql . WriteString ( ` AND a.epoch <= ? AND a.epoch_end >= ? ` )
params = append ( params , query . To , query . From )
}
2018-03-22 09:52:09 -05:00
2022-03-22 06:20:57 -05:00
if query . Type == "alert" {
sql . WriteString ( ` AND a.alert_id > 0 ` )
} else if query . Type == "annotation" {
sql . WriteString ( ` AND a.alert_id = 0 ` )
}
2016-09-08 04:25:45 -05:00
2022-03-22 06:20:57 -05:00
if len ( query . Tags ) > 0 {
keyValueFilters := [ ] string { }
2017-11-21 04:27:53 -06:00
2022-09-21 07:04:01 -05:00
tags := tag . ParseTagPairs ( query . Tags )
2022-03-22 06:20:57 -05:00
for _ , tag := range tags {
if tag . Value == "" {
2022-09-19 02:54:37 -05:00
keyValueFilters = append ( keyValueFilters , "(tag." + r . db . GetDialect ( ) . Quote ( "key" ) + " = ?)" )
2022-03-22 06:20:57 -05:00
params = append ( params , tag . Key )
} else {
2022-09-19 02:54:37 -05:00
keyValueFilters = append ( keyValueFilters , "(tag." + r . db . GetDialect ( ) . Quote ( "key" ) + " = ? AND tag." + r . db . GetDialect ( ) . Quote ( "value" ) + " = ?)" )
2022-03-22 06:20:57 -05:00
params = append ( params , tag . Key , tag . Value )
}
2017-10-07 03:31:39 -05:00
}
2016-08-30 02:32:56 -05:00
2022-03-22 06:20:57 -05:00
if len ( tags ) > 0 {
tagsSubQuery := fmt . Sprintf ( `
SELECT SUM ( 1 ) FROM annotation_tag at
INNER JOIN tag on tag . id = at . tag_id
WHERE at . annotation_id = a . id
AND (
% s
)
` , strings . Join ( keyValueFilters , " OR " ) )
if query . MatchAny {
sql . WriteString ( fmt . Sprintf ( " AND (%s) > 0 " , tagsSubQuery ) )
} else {
sql . WriteString ( fmt . Sprintf ( " AND (%s) = %d " , tagsSubQuery , len ( tags ) ) )
}
2018-09-11 08:50:04 -05:00
}
2016-09-14 01:36:44 -05:00
}
2016-08-30 02:32:56 -05:00
2023-11-14 17:11:01 -06:00
acFilter , err := r . getAccessControlFilter ( query . SignedInUser , accessResources )
2023-08-25 08:19:58 -05:00
if err != nil {
return err
2022-04-11 07:18:38 -05:00
}
2023-11-14 17:11:01 -06:00
sql . WriteString ( fmt . Sprintf ( " AND (%s)" , acFilter ) )
2022-04-11 07:18:38 -05:00
2022-03-22 06:20:57 -05:00
if query . Limit == 0 {
query . Limit = 100
}
2016-08-30 02:32:56 -05:00
2022-03-22 06:20:57 -05:00
// order of ORDER BY arguments match the order of a sql index for performance
2022-09-19 02:54:37 -05:00
sql . WriteString ( " ORDER BY a.org_id, a.epoch_end DESC, a.epoch DESC" + r . db . GetDialect ( ) . Limit ( query . Limit ) + " ) dt on dt.id = annotation.id" )
2023-04-06 03:16:15 -05:00
2022-03-22 06:20:57 -05:00
if err := sess . SQL ( sql . String ( ) , params ... ) . Find ( & items ) ; err != nil {
items = nil
return err
}
return nil
} ,
)
2016-08-01 03:07:00 -05:00
2022-03-22 06:20:57 -05:00
return items , err
2016-08-01 03:07:00 -05:00
}
2016-10-14 02:33:16 -05:00
2023-11-14 17:11:01 -06:00
func ( r * xormRepositoryImpl ) getAccessControlFilter ( user identity . Requester , accessResources * accesscontrol . AccessResources ) ( string , error ) {
var filters [ ] string
2023-04-06 03:16:15 -05:00
2023-11-14 17:11:01 -06:00
if _ , has := accessResources . ScopeTypes [ annotations . Organization . String ( ) ] ; has {
filters = append ( filters , "a.dashboard_id = 0" )
2022-04-11 07:18:38 -05:00
}
2023-10-06 04:59:48 -05:00
2023-11-14 17:11:01 -06:00
if _ , has := accessResources . ScopeTypes [ annotations . Dashboard . String ( ) ] ; has {
var dashboardIDs [ ] int64
for _ , id := range accessResources . Dashboards {
dashboardIDs = append ( dashboardIDs , id )
}
2022-04-11 07:18:38 -05:00
2023-11-14 17:11:01 -06:00
var inClause string
if len ( dashboardIDs ) == 0 {
inClause = "SELECT * FROM (SELECT 0 LIMIT 0) tt" // empty set
} else {
b := make ( [ ] byte , 0 , 3 * len ( dashboardIDs ) )
2023-04-06 03:16:15 -05:00
2023-11-14 17:11:01 -06:00
b = strconv . AppendInt ( b , dashboardIDs [ 0 ] , 10 )
for _ , num := range dashboardIDs [ 1 : ] {
b = append ( b , ',' )
b = strconv . AppendInt ( b , num , 10 )
2023-08-02 02:39:25 -05:00
}
2023-11-14 17:11:01 -06:00
inClause = string ( b )
2022-04-11 07:18:38 -05:00
}
2023-11-14 17:11:01 -06:00
filters = append ( filters , fmt . Sprintf ( "a.dashboard_id IN (%s)" , inClause ) )
2022-04-11 07:18:38 -05:00
}
2023-04-06 03:16:15 -05:00
2023-11-14 17:11:01 -06:00
return strings . Join ( filters , " OR " ) , nil
2022-04-11 07:18:38 -05:00
}
2022-09-22 07:27:48 -05:00
func ( r * xormRepositoryImpl ) Delete ( ctx context . Context , params * annotations . DeleteParams ) error {
2022-10-19 08:02:15 -05:00
return r . db . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2017-10-07 03:31:39 -05:00
var (
2021-12-21 12:04:56 -06:00
sql string
annoTagSQL string
2017-10-07 03:31:39 -05:00
)
2023-02-03 10:23:09 -06:00
r . log . Info ( "delete" , "orgId" , params . OrgID )
if params . ID != 0 {
2020-11-10 23:21:08 -06:00
annoTagSQL = "DELETE FROM annotation_tag WHERE annotation_id IN (SELECT id FROM annotation WHERE id = ? AND org_id = ?)"
2018-06-25 09:02:34 -05:00
sql = "DELETE FROM annotation WHERE id = ? AND org_id = ?"
2021-12-21 12:04:56 -06:00
2023-02-03 10:23:09 -06:00
if _ , err := sess . Exec ( annoTagSQL , params . ID , params . OrgID ) ; err != nil {
2021-12-21 12:04:56 -06:00
return err
}
2023-02-03 10:23:09 -06:00
if _ , err := sess . Exec ( sql , params . ID , params . OrgID ) ; err != nil {
2021-12-21 12:04:56 -06:00
return err
}
2017-10-07 03:31:39 -05:00
} else {
2020-11-10 23:21:08 -06:00
annoTagSQL = "DELETE FROM annotation_tag WHERE annotation_id IN (SELECT id FROM annotation WHERE dashboard_id = ? AND panel_id = ? AND org_id = ?)"
2018-06-25 09:02:34 -05:00
sql = "DELETE FROM annotation WHERE dashboard_id = ? AND panel_id = ? AND org_id = ?"
2019-03-04 09:57:29 -06:00
2023-02-03 10:23:09 -06:00
if _ , err := sess . Exec ( annoTagSQL , params . DashboardID , params . PanelID , params . OrgID ) ; err != nil {
2021-12-21 12:04:56 -06:00
return err
}
2019-03-04 09:57:29 -06:00
2023-02-03 10:23:09 -06:00
if _ , err := sess . Exec ( sql , params . DashboardID , params . PanelID , params . OrgID ) ; err != nil {
2021-12-21 12:04:56 -06:00
return err
}
2016-10-14 02:33:16 -05:00
}
return nil
} )
}
2021-06-30 06:42:54 -05:00
2022-09-22 07:27:48 -05:00
func ( r * xormRepositoryImpl ) GetTags ( ctx context . Context , query * annotations . TagsQuery ) ( annotations . FindTagsResult , error ) {
2022-03-25 12:23:09 -05:00
var items [ ] * annotations . Tag
2022-10-19 08:02:15 -05:00
err := r . db . WithDbSession ( ctx , func ( dbSession * db . Session ) error {
2022-03-25 12:23:09 -05:00
if query . Limit == 0 {
query . Limit = 100
}
2021-06-30 06:42:54 -05:00
2022-03-25 12:23:09 -05:00
var sql bytes . Buffer
params := make ( [ ] interface { } , 0 )
2022-09-19 02:54:37 -05:00
tagKey := ` tag. ` + r . db . GetDialect ( ) . Quote ( "key" )
tagValue := ` tag. ` + r . db . GetDialect ( ) . Quote ( "value" )
2021-06-30 06:42:54 -05:00
2022-03-25 12:23:09 -05:00
sql . WriteString ( `
2021-06-30 06:42:54 -05:00
SELECT
` + tagKey + ` ,
` + tagValue + ` ,
count ( * ) as count
FROM tag
INNER JOIN annotation_tag ON tag . id = annotation_tag . tag_id
2023-04-18 05:27:40 -05:00
INNER JOIN annotation ON annotation . id = annotation_tag . annotation_id
2021-06-30 06:42:54 -05:00
` )
2023-04-18 05:27:40 -05:00
sql . WriteString ( ` WHERE annotation.org_id = ? ` )
2022-03-25 12:23:09 -05:00
params = append ( params , query . OrgID )
2021-06-30 06:42:54 -05:00
2022-09-19 02:54:37 -05:00
sql . WriteString ( ` AND ( ` + tagKey + ` ` + r . db . GetDialect ( ) . LikeStr ( ) + ` ? OR ` + tagValue + ` ` + r . db . GetDialect ( ) . LikeStr ( ) + ` ?) ` )
2022-03-25 12:23:09 -05:00
params = append ( params , ` % ` + query . Tag + ` % ` , ` % ` + query . Tag + ` % ` )
2021-06-30 06:42:54 -05:00
2022-03-25 12:23:09 -05:00
sql . WriteString ( ` GROUP BY ` + tagKey + ` , ` + tagValue )
sql . WriteString ( ` ORDER BY ` + tagKey + ` , ` + tagValue )
2022-09-19 02:54:37 -05:00
sql . WriteString ( ` ` + r . db . GetDialect ( ) . Limit ( query . Limit ) )
2021-06-30 06:42:54 -05:00
2022-03-25 12:23:09 -05:00
err := dbSession . SQL ( sql . String ( ) , params ... ) . Find ( & items )
return err
} )
if err != nil {
2021-06-30 06:42:54 -05:00
return annotations . FindTagsResult { Tags : [ ] * annotations . TagsDTO { } } , err
}
tags := make ( [ ] * annotations . TagsDTO , 0 )
for _ , item := range items {
tag := item . Key
if len ( item . Value ) > 0 {
tag = item . Key + ":" + item . Value
}
tags = append ( tags , & annotations . TagsDTO {
Tag : tag ,
Count : item . Count ,
} )
}
return annotations . FindTagsResult { Tags : tags } , nil
}
2022-09-22 07:27:48 -05:00
2022-09-23 05:04:41 -05:00
func ( r * xormRepositoryImpl ) validateItem ( item * annotations . Item ) error {
if err := validateTimeRange ( item ) ; err != nil {
return err
}
if err := r . validateTagsLength ( item ) ; err != nil {
return err
}
return nil
}
func ( r * xormRepositoryImpl ) validateTagsLength ( item * annotations . Item ) error {
estimatedTagsLength := 1 // leading: [
for i , t := range item . Tags {
if i == 0 {
estimatedTagsLength += len ( t ) + 2 // quotes
} else {
estimatedTagsLength += len ( t ) + 3 // leading comma and quotes
}
}
estimatedTagsLength += 1 // trailing: ]
2023-11-14 17:11:01 -06:00
if estimatedTagsLength > int ( r . cfg . AnnotationMaximumTagsLength ) {
return annotations . ErrBaseTagLimitExceeded . Errorf ( "tags length (%d) exceeds the maximum allowed (%d): modify the configuration to increase it" , estimatedTagsLength , r . cfg . AnnotationMaximumTagsLength )
2022-09-23 05:04:41 -05:00
}
return nil
}
2022-09-22 07:27:48 -05:00
func ( r * xormRepositoryImpl ) CleanAnnotations ( ctx context . Context , cfg setting . AnnotationCleanupSettings , annotationType string ) ( int64 , error ) {
var totalAffected int64
if cfg . MaxAge > 0 {
2023-11-14 17:11:01 -06:00
cutoffDate := timeNow ( ) . Add ( - cfg . MaxAge ) . UnixNano ( ) / int64 ( time . Millisecond )
2022-09-22 07:27:48 -05:00
deleteQuery := ` DELETE FROM annotation WHERE id IN (SELECT id FROM (SELECT id FROM annotation WHERE %s AND created < %v ORDER BY id DESC %s) a) `
sql := fmt . Sprintf ( deleteQuery , annotationType , cutoffDate , r . db . GetDialect ( ) . Limit ( r . cfg . AnnotationCleanupJobBatchSize ) )
affected , err := r . executeUntilDoneOrCancelled ( ctx , sql )
totalAffected += affected
if err != nil {
return totalAffected , err
}
}
if cfg . MaxCount > 0 {
deleteQuery := ` DELETE FROM annotation WHERE id IN (SELECT id FROM (SELECT id FROM annotation WHERE %s ORDER BY id DESC %s) a) `
sql := fmt . Sprintf ( deleteQuery , annotationType , r . db . GetDialect ( ) . LimitOffset ( r . cfg . AnnotationCleanupJobBatchSize , cfg . MaxCount ) )
affected , err := r . executeUntilDoneOrCancelled ( ctx , sql )
totalAffected += affected
return totalAffected , err
}
return totalAffected , nil
}
func ( r * xormRepositoryImpl ) CleanOrphanedAnnotationTags ( ctx context . Context ) ( int64 , error ) {
deleteQuery := ` DELETE FROM annotation_tag WHERE id IN ( SELECT id FROM (SELECT id FROM annotation_tag WHERE NOT EXISTS (SELECT 1 FROM annotation a WHERE annotation_id = a.id) %s) a) `
sql := fmt . Sprintf ( deleteQuery , r . db . GetDialect ( ) . Limit ( r . cfg . AnnotationCleanupJobBatchSize ) )
return r . executeUntilDoneOrCancelled ( ctx , sql )
}
func ( r * xormRepositoryImpl ) executeUntilDoneOrCancelled ( ctx context . Context , sql string ) ( int64 , error ) {
var totalAffected int64
for {
select {
case <- ctx . Done ( ) :
return totalAffected , ctx . Err ( )
default :
var affected int64
2022-10-19 08:02:15 -05:00
err := r . db . WithDbSession ( ctx , func ( session * db . Session ) error {
2022-09-22 07:27:48 -05:00
res , err := session . Exec ( sql )
if err != nil {
return err
}
affected , err = res . RowsAffected ( )
totalAffected += affected
return err
} )
if err != nil {
return totalAffected , err
}
if affected == 0 {
return totalAffected , nil
}
}
}
}
2023-07-31 10:19:59 -05:00
type annotationTag struct {
AnnotationID int64 ` xorm:"annotation_id" `
TagID int64 ` xorm:"tag_id" `
}