diff --git a/promql/ast.go b/promql/ast.go index 973971d292..06bd972923 100644 --- a/promql/ast.go +++ b/promql/ast.go @@ -107,14 +107,8 @@ type Call struct { // MatrixSelector represents a Matrix selection. type MatrixSelector struct { - Name string - Range time.Duration - Offset time.Duration - LabelMatchers []*labels.Matcher - - // The unexpanded seriesSet populated at query preparation time. - unexpandedSeriesSet storage.SeriesSet - series []storage.Series + VectorSelector *VectorSelector + Range time.Duration } // SubqueryExpr represents a subquery. @@ -316,7 +310,9 @@ func Children(node Node) []Node { return []Node{n.Expr} case *UnaryExpr: return []Node{n.Expr} - case *MatrixSelector, *NumberLiteral, *StringLiteral, *VectorSelector: + case *MatrixSelector: + return []Node{n.VectorSelector} + case *NumberLiteral, *StringLiteral, *VectorSelector: // nothing to do return []Node{} default: diff --git a/promql/engine.go b/promql/engine.go index 26fe05b647..09ac982691 100644 --- a/promql/engine.go +++ b/promql/engine.go @@ -623,8 +623,8 @@ func (ng *Engine) populateSeries(ctx context.Context, q storage.Queryable, s *Ev if maxOffset < n.Range+subqOffset { maxOffset = n.Range + subqOffset } - if n.Offset+n.Range+subqOffset > maxOffset { - maxOffset = n.Offset + n.Range + subqOffset + if m := n.VectorSelector.Offset + n.Range + subqOffset; m > maxOffset { + maxOffset = m } } return nil @@ -639,6 +639,11 @@ func (ng *Engine) populateSeries(ctx context.Context, q storage.Queryable, s *Ev var warnings storage.Warnings + // Whenever a MatrixSelector is evaluated this variable is set to the corresponding range. + // The evaluation of the VectorSelector inside then evaluates the given range and unsets + // the variable. + var evalRange time.Duration + Inspect(s.Expr, func(node Node, path []Node) error { var set storage.SeriesSet var wrn storage.Warnings @@ -658,7 +663,16 @@ func (ng *Engine) populateSeries(ctx context.Context, q storage.Queryable, s *Ev switch n := node.(type) { case *VectorSelector: - params.Start = params.Start - durationMilliseconds(LookbackDelta) + if evalRange == 0 { + params.Start = params.Start - durationMilliseconds(LookbackDelta) + } else { + params.Range = durationMilliseconds(evalRange) + // For all matrix queries we want to ensure that we have (end-start) + range selected + // this way we have `range` data before the start time + params.Start = params.Start - durationMilliseconds(evalRange) + evalRange = 0 + } + params.Func = extractFuncFromPath(path) params.By, params.Grouping = extractGroupsFromPath(path) if n.Offset > 0 { @@ -676,24 +690,7 @@ func (ng *Engine) populateSeries(ctx context.Context, q storage.Queryable, s *Ev n.unexpandedSeriesSet = set case *MatrixSelector: - params.Func = extractFuncFromPath(path) - params.Range = durationMilliseconds(n.Range) - // For all matrix queries we want to ensure that we have (end-start) + range selected - // this way we have `range` data before the start time - params.Start = params.Start - durationMilliseconds(n.Range) - if n.Offset > 0 { - offsetMilliseconds := durationMilliseconds(n.Offset) - params.Start = params.Start - offsetMilliseconds - params.End = params.End - offsetMilliseconds - } - - set, wrn, err = querier.Select(params, n.LabelMatchers...) - warnings = append(warnings, wrn...) - if err != nil { - level.Error(ng.logger).Log("msg", "error selecting series set", "err", err) - return err - } - n.unexpandedSeriesSet = set + evalRange = n.Range } return nil }) @@ -734,14 +731,7 @@ func extractGroupsFromPath(p []Node) (bool, []string) { func checkForSeriesSetExpansion(ctx context.Context, expr Expr) { switch e := expr.(type) { case *MatrixSelector: - if e.series == nil { - series, err := expandSeriesSet(ctx, e.unexpandedSeriesSet) - if err != nil { - panic(err) - } else { - e.series = series - } - } + checkForSeriesSetExpansion(ctx, e.VectorSelector) case *VectorSelector: if e.series == nil { series, err := expandSeriesSet(ctx, e.unexpandedSeriesSet) @@ -1000,12 +990,14 @@ func (ev *evaluator) rangeEval(f func([]Value, *EvalNodeHelper) Vector, exprs .. func (ev *evaluator) evalSubquery(subq *SubqueryExpr) *MatrixSelector { val := ev.eval(subq).(Matrix) ms := &MatrixSelector{ - Range: subq.Range, - Offset: subq.Offset, - series: make([]storage.Series, 0, len(val)), + Range: subq.Range, + VectorSelector: &VectorSelector{ + Offset: subq.Offset, + series: make([]storage.Series, 0, len(val)), + }, } for _, s := range val { - ms.series = append(ms.series, NewStorageSeries(s)) + ms.VectorSelector.series = append(ms.VectorSelector.series, NewStorageSeries(s)) } return ms } @@ -1085,9 +1077,11 @@ func (ev *evaluator) eval(expr Expr) Value { } sel := e.Args[matrixArgIndex].(*MatrixSelector) + selVS := sel.VectorSelector + checkForSeriesSetExpansion(ev.ctx, sel) - mat := make(Matrix, 0, len(sel.series)) // Output matrix. - offset := durationMilliseconds(sel.Offset) + mat := make(Matrix, 0, len(selVS.series)) // Output matrix. + offset := durationMilliseconds(selVS.Offset) selRange := durationMilliseconds(sel.Range) stepRange := selRange if stepRange > ev.interval { @@ -1100,17 +1094,17 @@ func (ev *evaluator) eval(expr Expr) Value { enh := &EvalNodeHelper{out: make(Vector, 0, 1)} // Process all the calls for one time series at a time. it := storage.NewBuffer(selRange) - for i, s := range sel.series { + for i, s := range selVS.series { points = points[:0] it.Reset(s.Iterator()) ss := Series{ // For all range vector functions, the only change to the // output labels is dropping the metric name so just do // it once here. - Metric: dropMetricName(sel.series[i].Labels()), + Metric: dropMetricName(selVS.series[i].Labels()), Points: getPointSlice(numSteps), } - inMatrix[0].Metric = sel.series[i].Labels() + inMatrix[0].Metric = selVS.series[i].Labels() for ts, step := ev.startTimestamp, -1; ts <= ev.endTimestamp; ts += ev.interval { step++ // Set the non-matrix arguments. @@ -1409,20 +1403,22 @@ func (ev *evaluator) matrixSelector(node *MatrixSelector) Matrix { checkForSeriesSetExpansion(ev.ctx, node) var ( - offset = durationMilliseconds(node.Offset) + offset = durationMilliseconds(node.VectorSelector.Offset) maxt = ev.startTimestamp - offset mint = maxt - durationMilliseconds(node.Range) - matrix = make(Matrix, 0, len(node.series)) + matrix = make(Matrix, 0, len(node.VectorSelector.series)) ) it := storage.NewBuffer(durationMilliseconds(node.Range)) - for i, s := range node.series { + series := node.VectorSelector.series + + for i, s := range series { if err := contextDone(ev.ctx, "expression evaluation"); err != nil { ev.error(err) } it.Reset(s.Iterator()) ss := Series{ - Metric: node.series[i].Labels(), + Metric: series[i].Labels(), } ss.Points = ev.matrixIterSlice(it, mint, maxt, getPointSlice(16)) diff --git a/promql/functions.go b/promql/functions.go index 1475bb7386..88803f04c5 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -67,8 +67,8 @@ func extrapolatedRate(vals []Value, args Expressions, enh *EvalNodeHelper, isCou var ( samples = vals[0].(Matrix)[0] - rangeStart = enh.ts - durationMilliseconds(ms.Range+ms.Offset) - rangeEnd = enh.ts - durationMilliseconds(ms.Offset) + rangeStart = enh.ts - durationMilliseconds(ms.Range+ms.VectorSelector.Offset) + rangeEnd = enh.ts - durationMilliseconds(ms.VectorSelector.Offset) ) // No sense in trying to compute a rate without at least two points. Drop @@ -1243,7 +1243,7 @@ func createLabelsForAbsentFunction(expr Expr) labels.Labels { case *VectorSelector: lm = n.LabelMatchers case *MatrixSelector: - lm = n.LabelMatchers + lm = n.VectorSelector.LabelMatchers default: return m } diff --git a/promql/generated_parser.y b/promql/generated_parser.y index 98984b7c08..fb8d22b08a 100644 --- a/promql/generated_parser.y +++ b/promql/generated_parser.y @@ -384,9 +384,7 @@ matrix_selector : expr LEFT_BRACKET duration RIGHT_BRACKET yylex.(*parser).errorf("no offset modifiers allowed before range") } $$ = &MatrixSelector{ - Name: vs.Name, - Offset: vs.Offset, - LabelMatchers: vs.LabelMatchers, + VectorSelector: vs, Range: $3, } } diff --git a/promql/generated_parser.y.go b/promql/generated_parser.y.go index b3133db4fe..45dd8acbf1 100644 --- a/promql/generated_parser.y.go +++ b/promql/generated_parser.y.go @@ -184,7 +184,7 @@ const yyEofCode = 1 const yyErrCode = 2 const yyInitialStackSize = 16 -//line promql/generated_parser.y:641 +//line promql/generated_parser.y:639 //line yacctab:1 var yyExca = [...]int{ @@ -1119,15 +1119,13 @@ yydefault: yylex.(*parser).errorf("no offset modifiers allowed before range") } yyVAL.node = &MatrixSelector{ - Name: vs.Name, - Offset: vs.Offset, - LabelMatchers: vs.LabelMatchers, - Range: yyDollar[3].duration, + VectorSelector: vs, + Range: yyDollar[3].duration, } } case 67: yyDollar = yyS[yypt-6 : yypt+1] -//line promql/generated_parser.y:396 +//line promql/generated_parser.y:394 { yyVAL.node = &SubqueryExpr{ Expr: yyDollar[1].node.(Expr), @@ -1137,31 +1135,31 @@ yydefault: } case 68: yyDollar = yyS[yypt-6 : yypt+1] -//line promql/generated_parser.y:404 +//line promql/generated_parser.y:402 { yylex.(*parser).unexpected("subquery selector", "\"]\"") } case 69: yyDollar = yyS[yypt-5 : yypt+1] -//line promql/generated_parser.y:406 +//line promql/generated_parser.y:404 { yylex.(*parser).unexpected("subquery selector", "duration or \"]\"") } case 70: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/generated_parser.y:408 +//line promql/generated_parser.y:406 { yylex.(*parser).unexpected("subquery or range", "\":\" or \"]\"") } case 71: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/generated_parser.y:410 +//line promql/generated_parser.y:408 { yylex.(*parser).unexpected("subquery selector", "duration") } case 72: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/generated_parser.y:420 +//line promql/generated_parser.y:418 { if nl, ok := yyDollar[2].node.(*NumberLiteral); ok { if yyDollar[1].item.Typ == SUB { @@ -1174,164 +1172,164 @@ yydefault: } case 73: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/generated_parser.y:437 +//line promql/generated_parser.y:435 { yyVAL.node = yylex.(*parser).newVectorSelector(yyDollar[1].item.Val, yyDollar[2].matchers) } case 74: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:439 +//line promql/generated_parser.y:437 { yyVAL.node = yylex.(*parser).newVectorSelector(yyDollar[1].item.Val, nil) } case 75: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:441 +//line promql/generated_parser.y:439 { yyVAL.node = yylex.(*parser).newVectorSelector("", yyDollar[1].matchers) } case 76: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/generated_parser.y:445 +//line promql/generated_parser.y:443 { yyVAL.matchers = yyDollar[2].matchers } case 77: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/generated_parser.y:447 +//line promql/generated_parser.y:445 { yyVAL.matchers = yyDollar[2].matchers } case 78: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/generated_parser.y:449 +//line promql/generated_parser.y:447 { yyVAL.matchers = []*labels.Matcher{} } case 79: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/generated_parser.y:454 +//line promql/generated_parser.y:452 { yyVAL.matchers = append(yyDollar[1].matchers, yyDollar[3].matcher) } case 80: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:456 +//line promql/generated_parser.y:454 { yyVAL.matchers = []*labels.Matcher{yyDollar[1].matcher} } case 81: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/generated_parser.y:458 +//line promql/generated_parser.y:456 { yylex.(*parser).unexpected("label matching", "\",\" or \"}\"") } case 82: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/generated_parser.y:462 +//line promql/generated_parser.y:460 { yyVAL.matcher = yylex.(*parser).newLabelMatcher(yyDollar[1].item, yyDollar[2].item, yyDollar[3].item) } case 83: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/generated_parser.y:464 +//line promql/generated_parser.y:462 { yylex.(*parser).unexpected("label matching", "string") } case 84: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/generated_parser.y:466 +//line promql/generated_parser.y:464 { yylex.(*parser).unexpected("label matching", "label matching operator") } case 85: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:468 +//line promql/generated_parser.y:466 { yylex.(*parser).unexpected("label matching", "identifier or \"}\"") } case 86: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/generated_parser.y:476 +//line promql/generated_parser.y:474 { yyVAL.labels = append(yyDollar[2].labels, labels.Label{Name: labels.MetricName, Value: yyDollar[1].item.Val}) sort.Sort(yyVAL.labels) } case 87: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:478 +//line promql/generated_parser.y:476 { yyVAL.labels = yyDollar[1].labels } case 90: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/generated_parser.y:485 +//line promql/generated_parser.y:483 { yyVAL.labels = labels.New(yyDollar[2].labels...) } case 91: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/generated_parser.y:487 +//line promql/generated_parser.y:485 { yyVAL.labels = labels.New(yyDollar[2].labels...) } case 92: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/generated_parser.y:489 +//line promql/generated_parser.y:487 { yyVAL.labels = labels.New() } case 93: yyDollar = yyS[yypt-0 : yypt+1] -//line promql/generated_parser.y:491 +//line promql/generated_parser.y:489 { yyVAL.labels = labels.New() } case 94: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/generated_parser.y:495 +//line promql/generated_parser.y:493 { yyVAL.labels = append(yyDollar[1].labels, yyDollar[3].label) } case 95: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:497 +//line promql/generated_parser.y:495 { yyVAL.labels = []labels.Label{yyDollar[1].label} } case 96: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/generated_parser.y:499 +//line promql/generated_parser.y:497 { yylex.(*parser).unexpected("label set", "\",\" or \"}\"") } case 97: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/generated_parser.y:504 +//line promql/generated_parser.y:502 { yyVAL.label = labels.Label{Name: yyDollar[1].item.Val, Value: yylex.(*parser).unquoteString(yyDollar[3].item.Val)} } case 98: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/generated_parser.y:506 +//line promql/generated_parser.y:504 { yylex.(*parser).unexpected("label set", "string") } case 99: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/generated_parser.y:508 +//line promql/generated_parser.y:506 { yylex.(*parser).unexpected("label set", "\"=\"") } case 100: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:510 +//line promql/generated_parser.y:508 { yylex.(*parser).unexpected("label set", "identifier or \"}\"") } case 101: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/generated_parser.y:518 +//line promql/generated_parser.y:516 { yylex.(*parser).generatedParserResult = &seriesDescription{ labels: yyDollar[1].labels, @@ -1340,37 +1338,37 @@ yydefault: } case 102: yyDollar = yyS[yypt-0 : yypt+1] -//line promql/generated_parser.y:527 +//line promql/generated_parser.y:525 { yyVAL.series = []sequenceValue{} } case 103: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/generated_parser.y:529 +//line promql/generated_parser.y:527 { yyVAL.series = append(yyDollar[1].series, yyDollar[3].series...) } case 104: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/generated_parser.y:531 +//line promql/generated_parser.y:529 { yyVAL.series = yyDollar[1].series } case 105: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:533 +//line promql/generated_parser.y:531 { yylex.(*parser).unexpected("series values", "") } case 106: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:537 +//line promql/generated_parser.y:535 { yyVAL.series = []sequenceValue{{omitted: true}} } case 107: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/generated_parser.y:539 +//line promql/generated_parser.y:537 { yyVAL.series = []sequenceValue{} for i := uint64(0); i < yyDollar[3].uint; i++ { @@ -1379,13 +1377,13 @@ yydefault: } case 108: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:546 +//line promql/generated_parser.y:544 { yyVAL.series = []sequenceValue{{value: yyDollar[1].float}} } case 109: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/generated_parser.y:548 +//line promql/generated_parser.y:546 { yyVAL.series = []sequenceValue{} for i := uint64(0); i <= yyDollar[3].uint; i++ { @@ -1394,7 +1392,7 @@ yydefault: } case 110: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/generated_parser.y:555 +//line promql/generated_parser.y:553 { yyVAL.series = []sequenceValue{} for i := uint64(0); i <= yyDollar[4].uint; i++ { @@ -1404,7 +1402,7 @@ yydefault: } case 111: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:565 +//line promql/generated_parser.y:563 { if yyDollar[1].item.Val != "stale" { yylex.(*parser).unexpected("series values", "number or \"stale\"") @@ -1413,31 +1411,31 @@ yydefault: } case 154: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:595 +//line promql/generated_parser.y:593 { yyVAL.node = &NumberLiteral{yyDollar[1].float} } case 155: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:597 +//line promql/generated_parser.y:595 { yyVAL.float = yylex.(*parser).number(yyDollar[1].item.Val) } case 156: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/generated_parser.y:599 +//line promql/generated_parser.y:597 { yyVAL.float = yyDollar[2].float } case 157: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/generated_parser.y:600 +//line promql/generated_parser.y:598 { yyVAL.float = -yyDollar[2].float } case 158: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:604 +//line promql/generated_parser.y:602 { var err error yyVAL.uint, err = strconv.ParseUint(yyDollar[1].item.Val, 10, 64) @@ -1447,7 +1445,7 @@ yydefault: } case 159: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:614 +//line promql/generated_parser.y:612 { var err error yyVAL.duration, err = parseDuration(yyDollar[1].item.Val) @@ -1457,25 +1455,25 @@ yydefault: } case 160: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:624 +//line promql/generated_parser.y:622 { yyVAL.node = &StringLiteral{yyDollar[1].string} } case 161: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/generated_parser.y:626 +//line promql/generated_parser.y:624 { yyVAL.string = yylex.(*parser).unquoteString(yyDollar[1].item.Val) } case 162: yyDollar = yyS[yypt-0 : yypt+1] -//line promql/generated_parser.y:633 +//line promql/generated_parser.y:631 { yyVAL.duration = 0 } case 164: yyDollar = yyS[yypt-0 : yypt+1] -//line promql/generated_parser.y:637 +//line promql/generated_parser.y:635 { yyVAL.strings = nil } diff --git a/promql/parse.go b/promql/parse.go index c9d9af408a..e4d320d9e5 100644 --- a/promql/parse.go +++ b/promql/parse.go @@ -504,8 +504,10 @@ func (p *parser) checkType(node Node) (typ ValueType) { if ty != ValueTypeVector { p.errorf("subquery is only allowed on instant vector, got %s in %q instead", ty, n.String()) } + case *MatrixSelector: + p.checkType(n.VectorSelector) - case *NumberLiteral, *MatrixSelector, *StringLiteral, *VectorSelector: + case *NumberLiteral, *StringLiteral, *VectorSelector: // Nothing to do for terminals. default: @@ -581,7 +583,7 @@ func (p *parser) addOffset(e Node, offset time.Duration) { case *VectorSelector: offsetp = &s.Offset case *MatrixSelector: - offsetp = &s.Offset + offsetp = &s.VectorSelector.Offset case *SubqueryExpr: offsetp = &s.Offset default: diff --git a/promql/parse_test.go b/promql/parse_test.go index d663cc44c4..8c44652229 100644 --- a/promql/parse_test.go +++ b/promql/parse_test.go @@ -1028,63 +1028,76 @@ var testExpr = []struct { { input: "test[5s]", expected: &MatrixSelector{ - Name: "test", - Offset: 0, - Range: 5 * time.Second, - LabelMatchers: []*labels.Matcher{ - mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"), + VectorSelector: &VectorSelector{ + Name: "test", + Offset: 0, + LabelMatchers: []*labels.Matcher{ + mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"), + }, }, + + Range: 5 * time.Second, }, }, { input: "test[5m]", expected: &MatrixSelector{ - Name: "test", - Offset: 0, - Range: 5 * time.Minute, - LabelMatchers: []*labels.Matcher{ - mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"), + VectorSelector: &VectorSelector{ + Name: "test", + Offset: 0, + LabelMatchers: []*labels.Matcher{ + mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"), + }, }, + Range: 5 * time.Minute, }, }, { input: "test[5h] OFFSET 5m", expected: &MatrixSelector{ - Name: "test", - Offset: 5 * time.Minute, - Range: 5 * time.Hour, - LabelMatchers: []*labels.Matcher{ - mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"), + VectorSelector: &VectorSelector{ + Name: "test", + Offset: 5 * time.Minute, + LabelMatchers: []*labels.Matcher{ + mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"), + }, }, + Range: 5 * time.Hour, }, }, { input: "test[5d] OFFSET 10s", expected: &MatrixSelector{ - Name: "test", - Offset: 10 * time.Second, - Range: 5 * 24 * time.Hour, - LabelMatchers: []*labels.Matcher{ - mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"), + VectorSelector: &VectorSelector{ + Name: "test", + Offset: 10 * time.Second, + LabelMatchers: []*labels.Matcher{ + mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"), + }, }, + Range: 5 * 24 * time.Hour, }, }, { input: "test[5w] offset 2w", expected: &MatrixSelector{ - Name: "test", - Offset: 14 * 24 * time.Hour, - Range: 5 * 7 * 24 * time.Hour, - LabelMatchers: []*labels.Matcher{ - mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"), + VectorSelector: &VectorSelector{ + Name: "test", + Offset: 14 * 24 * time.Hour, + LabelMatchers: []*labels.Matcher{ + mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"), + }, }, + Range: 5 * 7 * 24 * time.Hour, }, }, { input: `test{a="b"}[5y] OFFSET 3d`, expected: &MatrixSelector{ - Name: "test", - Offset: 3 * 24 * time.Hour, - Range: 5 * 365 * 24 * time.Hour, - LabelMatchers: []*labels.Matcher{ - mustLabelMatcher(labels.MatchEqual, "a", "b"), - mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"), + VectorSelector: &VectorSelector{ + Name: "test", + Offset: 3 * 24 * time.Hour, + LabelMatchers: []*labels.Matcher{ + mustLabelMatcher(labels.MatchEqual, "a", "b"), + mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "test"), + }, }, + Range: 5 * 365 * 24 * time.Hour, }, }, { input: `foo[5mm]`, @@ -1378,9 +1391,11 @@ var testExpr = []struct { Func: mustGetFunction("rate"), Args: Expressions{ &MatrixSelector{ - Name: "some_metric", - LabelMatchers: []*labels.Matcher{ - mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"), + VectorSelector: &VectorSelector{ + Name: "some_metric", + LabelMatchers: []*labels.Matcher{ + mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"), + }, }, Range: 5 * time.Minute, }, @@ -1535,12 +1550,14 @@ var testExpr = []struct { Func: mustGetFunction("rate"), Args: Expressions{ &MatrixSelector{ - Name: "foo", - Range: 2 * time.Second, - LabelMatchers: []*labels.Matcher{ - mustLabelMatcher(labels.MatchEqual, "bar", "baz"), - mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"), + VectorSelector: &VectorSelector{ + Name: "foo", + LabelMatchers: []*labels.Matcher{ + mustLabelMatcher(labels.MatchEqual, "bar", "baz"), + mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"), + }, }, + Range: 2 * time.Second, }, }, }, @@ -1560,12 +1577,14 @@ var testExpr = []struct { Func: mustGetFunction("rate"), Args: Expressions{ &MatrixSelector{ - Name: "foo", - Range: 2 * time.Second, - LabelMatchers: []*labels.Matcher{ - mustLabelMatcher(labels.MatchEqual, "bar", "baz"), - mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"), + VectorSelector: &VectorSelector{ + Name: "foo", + LabelMatchers: []*labels.Matcher{ + mustLabelMatcher(labels.MatchEqual, "bar", "baz"), + mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"), + }, }, + Range: 2 * time.Second, }, }, }, @@ -1587,12 +1606,14 @@ var testExpr = []struct { Func: mustGetFunction("rate"), Args: Expressions{ &MatrixSelector{ - Name: "foo", - Range: 2 * time.Second, - LabelMatchers: []*labels.Matcher{ - mustLabelMatcher(labels.MatchEqual, "bar", "baz"), - mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"), + VectorSelector: &VectorSelector{ + Name: "foo", + LabelMatchers: []*labels.Matcher{ + mustLabelMatcher(labels.MatchEqual, "bar", "baz"), + mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"), + }, }, + Range: 2 * time.Second, }, }, }, diff --git a/promql/printer.go b/promql/printer.go index 846353c88d..0f6800ae27 100644 --- a/promql/printer.go +++ b/promql/printer.go @@ -112,14 +112,16 @@ func (node *Call) String() string { } func (node *MatrixSelector) String() string { - vecSelector := &VectorSelector{ - Name: node.Name, - LabelMatchers: node.LabelMatchers, - } + // Copy the Vector selector before changing the offset + var vecSelector VectorSelector = *node.VectorSelector offset := "" - if node.Offset != time.Duration(0) { - offset = fmt.Sprintf(" offset %s", model.Duration(node.Offset)) + if vecSelector.Offset != time.Duration(0) { + offset = fmt.Sprintf(" offset %s", model.Duration(vecSelector.Offset)) } + + // Do not print the offset twice. + vecSelector.Offset = 0 + return fmt.Sprintf("%s[%s]%s", vecSelector.String(), model.Duration(node.Range), offset) }