mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 22:12:08 +08:00
SQL Expressions / Alerting: Do not allow duplicates (#103394)
This commit is contained in:
@ -182,6 +182,20 @@ func totalCells(frames []*data.Frame) (total int64) {
|
||||
return
|
||||
}
|
||||
|
||||
// extractNumberSetFromSQLForAlerting converts a data frame produced by a SQL expression
|
||||
// into a slice of mathexp.Number values for use in alerting.
|
||||
//
|
||||
// This function enforces strict semantics: each row must have exactly one numeric value
|
||||
// and a unique label set. If any label set appears more than once, an error is returned.
|
||||
//
|
||||
// It is the responsibility of the SQL query to ensure uniqueness — for example, by
|
||||
// applying GROUP BY or aggregation clauses. This function will not deduplicate rows;
|
||||
// it will reject the entire input if any duplicates are present.
|
||||
//
|
||||
// Returns an error if:
|
||||
// - No numeric field is found.
|
||||
// - More than one numeric field exists.
|
||||
// - Any label set appears more than once.
|
||||
func extractNumberSetFromSQLForAlerting(frame *data.Frame) ([]mathexp.Number, error) {
|
||||
var (
|
||||
numericField *data.Field
|
||||
@ -202,7 +216,13 @@ func extractNumberSetFromSQLForAlerting(frame *data.Frame) ([]mathexp.Number, er
|
||||
return nil, fmt.Errorf("no numeric field found in frame")
|
||||
}
|
||||
|
||||
numbers := make([]mathexp.Number, frame.Rows())
|
||||
type row struct {
|
||||
value float64
|
||||
labels data.Labels
|
||||
}
|
||||
rows := make([]row, 0, frame.Rows())
|
||||
counts := map[data.Fingerprint]int{}
|
||||
labelMap := map[data.Fingerprint]string{}
|
||||
|
||||
for i := 0; i < frame.Rows(); i++ {
|
||||
val, err := numericField.FloatAt(i)
|
||||
@ -227,10 +247,32 @@ func extractNumberSetFromSQLForAlerting(frame *data.Frame) ([]mathexp.Number, er
|
||||
}
|
||||
}
|
||||
|
||||
n := mathexp.NewNumber(numericField.Name, labels)
|
||||
fp := labels.Fingerprint()
|
||||
counts[fp]++
|
||||
labelMap[fp] = labels.String()
|
||||
|
||||
rows = append(rows, row{value: val, labels: labels})
|
||||
}
|
||||
|
||||
// Check for any duplicates
|
||||
duplicates := make([]string, 0)
|
||||
for fp, count := range counts {
|
||||
if count > 1 {
|
||||
duplicates = append(duplicates, labelMap[fp])
|
||||
}
|
||||
}
|
||||
|
||||
if len(duplicates) > 0 {
|
||||
return nil, makeDuplicateStringColumnError(duplicates)
|
||||
}
|
||||
|
||||
// Build final result
|
||||
numbers := make([]mathexp.Number, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
n := mathexp.NewNumber(numericField.Name, r.labels)
|
||||
n.Frame.Fields[0].Config = numericField.Config
|
||||
n.SetValue(&val)
|
||||
numbers[i] = n
|
||||
n.SetValue(&r.value)
|
||||
numbers = append(numbers, n)
|
||||
}
|
||||
|
||||
return numbers, nil
|
||||
|
Reference in New Issue
Block a user