Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 95be5d9

Browse files
authored
cmd/bosun: setvariant type to enable generics for expr funcs
1 parent 32566c5 commit 95be5d9

File tree

8 files changed

+169
-66
lines changed

8 files changed

+169
-66
lines changed

cmd/bosun/expr/expr.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,6 @@ func (e *State) union(a, b *Results, expression string) []*Union {
412412
var group opentsdb.TagSet
413413
for _, ra := range a.Results {
414414
for _, rb := range b.Results {
415-
416415
if ra.Group.Equal(rb.Group) || len(ra.Group) == 0 || len(rb.Group) == 0 {
417416
g := ra.Group
418417
if len(ra.Group) == 0 {
@@ -759,7 +758,7 @@ func (e *State) walkFunc(node *parse.FuncNode, T miniprofiler.Timer) *Results {
759758
} else {
760759
argType = node.F.Args[i]
761760
}
762-
if f, ok := v.(float64); ok && argType == models.TypeNumberSet {
761+
if f, ok := v.(float64); ok && (argType == models.TypeNumberSet || argType == models.TypeVariantSet) {
763762
v = fromScalar(f)
764763
}
765764
in = append(in, reflect.ValueOf(v))

cmd/bosun/expr/expr_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,79 @@ func TestQueryExpr(t *testing.T) {
258258
}
259259
}
260260

261+
func TestSetVariant(t *testing.T) {
262+
series := `series("key1=a,key2=b", 0, 1, 1, 3)`
263+
seriesAbs := `series("", 0, 1, 1, -3)`
264+
tests := []exprInOut{
265+
{
266+
fmt.Sprintf(`addtags(addtags(%v, "key3=a"), "key4=b") + 1`, series),
267+
Results{
268+
Results: ResultSlice{
269+
&Result{
270+
Value: Series{
271+
time.Unix(0, 0): 2,
272+
time.Unix(1, 0): 4,
273+
},
274+
Group: opentsdb.TagSet{"key1": "a", "key2": "b", "key3": "a", "key4": "b"},
275+
},
276+
},
277+
},
278+
false,
279+
},
280+
{
281+
fmt.Sprintf(`addtags(addtags(avg(%v + 1), "key3=a"), "key4=b") + 1`, series),
282+
Results{
283+
Results: ResultSlice{
284+
&Result{
285+
Value: Number(4),
286+
Group: opentsdb.TagSet{"key1": "a", "key2": "b", "key3": "a", "key4": "b"},
287+
},
288+
},
289+
},
290+
false,
291+
},
292+
{
293+
fmt.Sprintf(`avg(addtags(addtags(avg(%v + 1), "key3=a"), "key4=b")) + 1`, series),
294+
Results{},
295+
true,
296+
},
297+
298+
{
299+
fmt.Sprintf(`1 + abs(%v)`, seriesAbs),
300+
Results{
301+
Results: ResultSlice{
302+
&Result{
303+
Value: Series{
304+
time.Unix(0, 0): 2,
305+
time.Unix(1, 0): 4,
306+
},
307+
Group: opentsdb.TagSet{},
308+
},
309+
},
310+
},
311+
false,
312+
},
313+
{
314+
fmt.Sprintf(`1 + abs(avg(%v))`, seriesAbs),
315+
Results{
316+
Results: ResultSlice{
317+
&Result{
318+
Value: Number(2),
319+
Group: opentsdb.TagSet{},
320+
},
321+
},
322+
},
323+
false,
324+
},
325+
}
326+
for _, test := range tests {
327+
err := testExpression(test)
328+
if err != nil {
329+
t.Error(err)
330+
}
331+
}
332+
}
333+
261334
func TestSeriesOperations(t *testing.T) {
262335
seriesA := `series("key=a", 0, 1, 1, 2, 2, 1, 3, 4)`
263336
seriesB := `series("key=a", 0, 1, 2, 0, 3, 4)`

cmd/bosun/expr/funcs.go

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -202,22 +202,22 @@ var builtins = map[string]parse.Func{
202202

203203
// Group functions
204204
"addtags": {
205-
Args: []models.FuncType{models.TypeSeriesSet, models.TypeString},
206-
Return: models.TypeSeriesSet,
207-
Tags: tagRename,
208-
F: AddTags,
205+
Args: []models.FuncType{models.TypeVariantSet, models.TypeString},
206+
VariantReturn: true,
207+
Tags: tagRename,
208+
F: AddTags,
209209
},
210210
"rename": {
211-
Args: []models.FuncType{models.TypeSeriesSet, models.TypeString},
212-
Return: models.TypeSeriesSet,
213-
Tags: tagRename,
214-
F: Rename,
211+
Args: []models.FuncType{models.TypeVariantSet, models.TypeString},
212+
VariantReturn: true,
213+
Tags: tagRename,
214+
F: Rename,
215215
},
216216
"remove": {
217-
Args: []models.FuncType{models.TypeSeriesSet, models.TypeString},
218-
Return: models.TypeSeriesSet,
219-
Tags: tagRemove,
220-
F: Remove,
217+
Args: []models.FuncType{models.TypeVariantSet, models.TypeString},
218+
VariantReturn: true,
219+
Tags: tagRemove,
220+
F: Remove,
221221
},
222222
"t": {
223223
Args: []models.FuncType{models.TypeNumberSet, models.TypeString},
@@ -234,10 +234,10 @@ var builtins = map[string]parse.Func{
234234
// Other functions
235235

236236
"abs": {
237-
Args: []models.FuncType{models.TypeNumberSet},
238-
Return: models.TypeNumberSet,
239-
Tags: tagFirst,
240-
F: Abs,
237+
Args: []models.FuncType{models.TypeVariantSet},
238+
VariantReturn: true,
239+
Tags: tagFirst,
240+
F: Abs,
241241
},
242242
"crop": {
243243
Args: []models.FuncType{models.TypeSeriesSet, models.TypeNumberSet, models.TypeNumberSet},
@@ -303,16 +303,16 @@ var builtins = map[string]parse.Func{
303303
F: Epoch,
304304
},
305305
"filter": {
306-
Args: []models.FuncType{models.TypeSeriesSet, models.TypeNumberSet},
307-
Return: models.TypeSeriesSet,
308-
Tags: tagFirst,
309-
F: Filter,
306+
Args: []models.FuncType{models.TypeVariantSet, models.TypeNumberSet},
307+
VariantReturn: true,
308+
Tags: tagFirst,
309+
F: Filter,
310310
},
311311
"limit": {
312-
Args: []models.FuncType{models.TypeNumberSet, models.TypeScalar},
313-
Return: models.TypeNumberSet,
314-
Tags: tagFirst,
315-
F: Limit,
312+
Args: []models.FuncType{models.TypeVariantSet, models.TypeScalar},
313+
VariantReturn: true,
314+
Tags: tagFirst,
315+
F: Limit,
316316
},
317317
"nv": {
318318
Args: []models.FuncType{models.TypeNumberSet, models.TypeScalar},
@@ -553,27 +553,27 @@ func Sort(e *State, T miniprofiler.Timer, series *Results, order string) (*Resul
553553
return series, nil
554554
}
555555

556-
func Limit(e *State, T miniprofiler.Timer, series *Results, v float64) (*Results, error) {
556+
func Limit(e *State, T miniprofiler.Timer, set *Results, v float64) (*Results, error) {
557557
i := int(v)
558-
if len(series.Results) > i {
559-
series.Results = series.Results[:i]
558+
if len(set.Results) > i {
559+
set.Results = set.Results[:i]
560560
}
561-
return series, nil
561+
return set, nil
562562
}
563563

564-
func Filter(e *State, T miniprofiler.Timer, series *Results, number *Results) (*Results, error) {
564+
func Filter(e *State, T miniprofiler.Timer, set *Results, numberSet *Results) (*Results, error) {
565565
var ns ResultSlice
566-
for _, sr := range series.Results {
567-
for _, nr := range number.Results {
566+
for _, sr := range set.Results {
567+
for _, nr := range numberSet.Results {
568568
if sr.Group.Subset(nr.Group) || nr.Group.Subset(sr.Group) {
569569
if nr.Value.Value().(Number) != 0 {
570570
ns = append(ns, sr)
571571
}
572572
}
573573
}
574574
}
575-
series.Results = ns
576-
return series, nil
575+
set.Results = ns
576+
return set, nil
577577
}
578578

579579
func Tail(e *State, T miniprofiler.Timer, series *Results, number *Results) (*Results, error) {
@@ -634,20 +634,20 @@ func Merge(e *State, T miniprofiler.Timer, series ...*Results) (*Results, error)
634634
return res, nil
635635
}
636636

637-
func Remove(e *State, T miniprofiler.Timer, seriesSet *Results, tagKey string) (*Results, error) {
637+
func Remove(e *State, T miniprofiler.Timer, set *Results, tagKey string) (*Results, error) {
638638
seen := make(map[string]bool)
639-
for _, r := range seriesSet.Results {
639+
for _, r := range set.Results {
640640
if _, ok := r.Group[tagKey]; ok {
641641
delete(r.Group, tagKey)
642642
if _, ok := seen[r.Group.String()]; ok {
643-
return seriesSet, fmt.Errorf("duplicate group would result from removing tag key: %v", tagKey)
643+
return set, fmt.Errorf("duplicate group would result from removing tag key: %v", tagKey)
644644
}
645645
seen[r.Group.String()] = true
646646
} else {
647-
return seriesSet, fmt.Errorf("tag key %v not found in result", tagKey)
647+
return set, fmt.Errorf("tag key %v not found in result", tagKey)
648648
}
649649
}
650-
return seriesSet, nil
650+
return set, nil
651651
}
652652

653653
func LeftJoin(e *State, T miniprofiler.Timer, keysCSV, columnsCSV string, rowData ...*Results) (*Results, error) {
@@ -832,11 +832,18 @@ func reduce(e *State, T miniprofiler.Timer, series *Results, F func(Series, ...f
832832
return match(f, series, args...)
833833
}
834834

835-
func Abs(e *State, T miniprofiler.Timer, series *Results) *Results {
836-
for _, s := range series.Results {
837-
s.Value = Number(math.Abs(float64(s.Value.Value().(Number))))
835+
func Abs(e *State, T miniprofiler.Timer, set *Results) *Results {
836+
for _, s := range set.Results {
837+
switch s.Type() {
838+
case models.TypeNumberSet:
839+
s.Value = Number(math.Abs(float64(s.Value.Value().(Number))))
840+
case models.TypeSeriesSet:
841+
for k, v := range s.Value.Value().(Series) {
842+
s.Value.Value().(Series)[k] = math.Abs(v)
843+
}
844+
}
838845
}
839-
return series
846+
return set
840847
}
841848

842849
func Diff(e *State, T miniprofiler.Timer, series *Results) (r *Results, err error) {
@@ -1152,14 +1159,14 @@ func percentile(dps Series, args ...float64) (a float64) {
11521159
return x[int(i)]
11531160
}
11541161

1155-
func Rename(e *State, T miniprofiler.Timer, series *Results, s string) (*Results, error) {
1162+
func Rename(e *State, T miniprofiler.Timer, set *Results, s string) (*Results, error) {
11561163
for _, section := range strings.Split(s, ",") {
11571164
kv := strings.Split(section, "=")
11581165
if len(kv) != 2 {
11591166
return nil, fmt.Errorf("error passing groups")
11601167
}
11611168
oldKey, newKey := kv[0], kv[1]
1162-
for _, res := range series.Results {
1169+
for _, res := range set.Results {
11631170
for tag, v := range res.Group {
11641171
if oldKey == tag {
11651172
if _, ok := res.Group[newKey]; ok {
@@ -1172,19 +1179,19 @@ func Rename(e *State, T miniprofiler.Timer, series *Results, s string) (*Results
11721179
}
11731180
}
11741181
}
1175-
return series, nil
1182+
return set, nil
11761183
}
11771184

1178-
func AddTags(e *State, T miniprofiler.Timer, series *Results, s string) (*Results, error) {
1185+
func AddTags(e *State, T miniprofiler.Timer, set *Results, s string) (*Results, error) {
11791186
if s == "" {
1180-
return series, nil
1187+
return set, nil
11811188
}
11821189
tagSetToAdd, err := opentsdb.ParseTags(s)
11831190
if err != nil {
11841191
return nil, err
11851192
}
11861193
for tagKey, tagValue := range tagSetToAdd {
1187-
for _, res := range series.Results {
1194+
for _, res := range set.Results {
11881195
if res.Group == nil {
11891196
res.Group = make(opentsdb.TagSet)
11901197
}
@@ -1194,7 +1201,7 @@ func AddTags(e *State, T miniprofiler.Timer, series *Results, s string) (*Result
11941201
res.Group[tagKey] = tagValue
11951202
}
11961203
}
1197-
return series, nil
1204+
return set, nil
11981205
}
11991206

12001207
func Ungroup(e *State, T miniprofiler.Timer, d *Results) (*Results, error) {

cmd/bosun/expr/parse/node.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ type FuncNode struct {
7070
NodeType
7171
Pos
7272
Name string
73-
F Func
73+
F *Func
7474
Args []Node
7575
Prefix string
7676
}
7777

7878
func newFunc(pos Pos, name string, f Func) *FuncNode {
79-
return &FuncNode{NodeType: NodeFunc, Pos: pos, Name: name, F: f}
79+
return &FuncNode{NodeType: NodeFunc, Pos: pos, Name: name, F: &f}
8080
}
8181

8282
func (f *FuncNode) append(arg Node) {
@@ -133,7 +133,12 @@ func (f *FuncNode) Check(t *Tree) error {
133133
}
134134
argType := arg.Return()
135135
if funcType == models.TypeNumberSet && argType == models.TypeScalar {
136-
// Scalars are promoted to NumberSets during execution.
136+
argType = models.TypeNumberSet // Scalars are promoted to NumberSets during execution.
137+
}
138+
if funcType == models.TypeVariantSet {
139+
if !(argType == models.TypeNumberSet || argType == models.TypeSeriesSet || argType == models.TypeScalar) {
140+
return fmt.Errorf("parse: expected %v or %v for argument %v, got %v", models.TypeNumberSet, models.TypeSeriesSet, i, argType)
141+
}
137142
} else if funcType != argType {
138143
return fmt.Errorf("parse: expected %v, got %v for argument %v (%v)", funcType, argType, i, arg.String())
139144
}

cmd/bosun/expr/parse/parse.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type Func struct {
4242
MapFunc bool // Func is only valid in map expressions
4343
PrefixEnabled bool
4444
PrefixKey bool
45+
VariantReturn bool
4546
Check func(*Tree, *FuncNode) error
4647
}
4748

@@ -198,6 +199,12 @@ func (t *Tree) startParse(funcs []map[string]Func, lex *lexer) {
198199
t.funcs = funcs
199200
for _, funcMap := range funcs {
200201
for name, f := range funcMap {
202+
if f.VariantReturn {
203+
if f.Tags == nil {
204+
panic(fmt.Errorf("%v: expected Tags definition: got nil", name))
205+
}
206+
continue
207+
}
201208
switch f.Return {
202209
case models.TypeSeriesSet, models.TypeNumberSet:
203210
if f.Tags == nil {
@@ -374,7 +381,11 @@ func (t *Tree) Func() (f *FuncNode) {
374381
switch token = t.next(); token.typ {
375382
default:
376383
t.backup()
377-
f.append(t.O())
384+
node := t.O()
385+
f.append(node)
386+
if len(f.Args) == 1 && f.F.VariantReturn {
387+
f.F.Return = node.Return()
388+
}
378389
case itemTripleQuotedString:
379390
f.append(newString(token.pos, token.val, token.val[3:len(token.val)-3]))
380391
case itemString:

0 commit comments

Comments
 (0)