From 69edd8709ba7d4b2152eab5fb208a3d74b4a81e1 Mon Sep 17 00:00:00 2001 From: zenador Date: Fri, 15 Sep 2023 00:57:31 +0800 Subject: [PATCH] Add warnings (and annotations) to PromQL query results (#12152) Return annotations (warnings and infos) from PromQL queries This generalizes the warnings we have already used before (but only for problems with remote read) as "annotations". Annotations can be warnings or infos (the latter could be false positives). We do not treat them different in the API for now and return them all as "warnings". It would be easy to distinguish them and return infos separately, should that appear useful in the future. The new annotations are then used to create a lot of warnings or infos during PromQL evaluations. Partially these are things we have wanted for a long time (e.g. inform the user that they have applied `rate` to a metric that doesn't look like a counter), but the new native histograms have created even more needs for those annotations (e.g. if a query tries to aggregate float numbers with histograms). The annotations added here are not yet complete. A prominent example would be a warning about a range too short for a rate calculation. But such a warnings is more tricky to create with good fidelity and we will tackle it later. Another TODO is to take annotations into account when evaluating recording rules. --------- Signed-off-by: Jeanette Tan --- cmd/promtool/tsdb.go | 2 +- promql/engine.go | 97 +++--- promql/engine_test.go | 128 +++---- promql/functions.go | 508 +++++++++++++++------------- promql/parser/ast.go | 78 +++-- promql/parser/generated_parser.y | 5 +- promql/parser/generated_parser.y.go | 277 +++++++-------- promql/parser/lex.go | 32 +- promql/parser/lex_test.go | 4 +- promql/parser/parse.go | 51 +-- promql/parser/parse_test.go | 472 +++++++++++++------------- promql/parser/posrange/posrange.go | 54 +++ promql/test.go | 3 +- promql/value.go | 4 +- storage/fanout_test.go | 15 +- storage/generic.go | 5 +- storage/interface.go | 39 ++- storage/lazy.go | 24 +- storage/merge.go | 23 +- storage/merge_test.go | 50 ++- storage/noop.go | 13 +- storage/remote/codec.go | 9 +- storage/remote/codec_test.go | 3 +- storage/remote/read.go | 5 +- storage/remote/read_handler.go | 3 +- storage/remote/read_test.go | 4 +- storage/secondary.go | 11 +- tsdb/db_test.go | 7 +- tsdb/querier.go | 7 +- tsdb/querier_test.go | 25 +- util/annotations/annotations.go | 165 +++++++++ web/api/v1/api.go | 27 +- web/api/v1/api_test.go | 6 +- web/api/v1/errors_test.go | 7 +- 34 files changed, 1204 insertions(+), 959 deletions(-) create mode 100644 promql/parser/posrange/posrange.go create mode 100644 util/annotations/annotations.go diff --git a/cmd/promtool/tsdb.go b/cmd/promtool/tsdb.go index 1688875ef..bda047e36 100644 --- a/cmd/promtool/tsdb.go +++ b/cmd/promtool/tsdb.go @@ -662,7 +662,7 @@ func dumpSamples(ctx context.Context, path string, mint, maxt int64, match strin } if ws := ss.Warnings(); len(ws) > 0 { - return tsdb_errors.NewMulti(ws...).Err() + return tsdb_errors.NewMulti(ws.AsErrors()...).Err() } if ss.Err() != nil { diff --git a/promql/engine.go b/promql/engine.go index 13fcd6762..90a819188 100644 --- a/promql/engine.go +++ b/promql/engine.go @@ -44,6 +44,7 @@ import ( "github.com/prometheus/prometheus/promql/parser" "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/tsdb/chunkenc" + "github.com/prometheus/prometheus/util/annotations" "github.com/prometheus/prometheus/util/stats" "github.com/prometheus/prometheus/util/zeropool" ) @@ -573,7 +574,7 @@ func (ng *Engine) newTestQuery(f func(context.Context) error) Query { // // At this point per query only one EvalStmt is evaluated. Alert and record // statements are not handled by the Engine. -func (ng *Engine) exec(ctx context.Context, q *query) (v parser.Value, ws storage.Warnings, err error) { +func (ng *Engine) exec(ctx context.Context, q *query) (v parser.Value, ws annotations.Annotations, err error) { ng.metrics.currentQueries.Inc() defer func() { ng.metrics.currentQueries.Dec() @@ -666,7 +667,7 @@ func durationMilliseconds(d time.Duration) int64 { } // execEvalStmt evaluates the expression of an evaluation statement for the given time range. -func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.EvalStmt) (parser.Value, storage.Warnings, error) { +func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.EvalStmt) (parser.Value, annotations.Annotations, error) { prepareSpanTimer, ctxPrepare := query.stats.GetSpanTimer(ctx, stats.QueryPreparationTime, ng.metrics.queryPrepareTime) mint, maxt := ng.findMinMaxTime(s) querier, err := query.queryable.Querier(mint, maxt) @@ -952,7 +953,7 @@ func extractGroupsFromPath(p []parser.Node) (bool, []string) { return false, nil } -func checkAndExpandSeriesSet(ctx context.Context, expr parser.Expr) (storage.Warnings, error) { +func checkAndExpandSeriesSet(ctx context.Context, expr parser.Expr) (annotations.Annotations, error) { switch e := expr.(type) { case *parser.MatrixSelector: return checkAndExpandSeriesSet(ctx, e.VectorSelector) @@ -967,7 +968,7 @@ func checkAndExpandSeriesSet(ctx context.Context, expr parser.Expr) (storage.War return nil, nil } -func expandSeriesSet(ctx context.Context, it storage.SeriesSet) (res []storage.Series, ws storage.Warnings, err error) { +func expandSeriesSet(ctx context.Context, it storage.SeriesSet) (res []storage.Series, ws annotations.Annotations, err error) { for it.Next() { select { case <-ctx.Done(): @@ -981,7 +982,7 @@ func expandSeriesSet(ctx context.Context, it storage.SeriesSet) (res []storage.S type errWithWarnings struct { err error - warnings storage.Warnings + warnings annotations.Annotations } func (e errWithWarnings) Error() string { return e.err.Error() } @@ -1016,7 +1017,7 @@ func (ev *evaluator) error(err error) { } // recover is the handler that turns panics into returns from the top level of evaluation. -func (ev *evaluator) recover(expr parser.Expr, ws *storage.Warnings, errp *error) { +func (ev *evaluator) recover(expr parser.Expr, ws *annotations.Annotations, errp *error) { e := recover() if e == nil { return @@ -1032,7 +1033,7 @@ func (ev *evaluator) recover(expr parser.Expr, ws *storage.Warnings, errp *error *errp = fmt.Errorf("unexpected error: %w", err) case errWithWarnings: *errp = err.err - *ws = append(*ws, err.warnings...) + ws.Merge(err.warnings) case error: *errp = err default: @@ -1040,7 +1041,7 @@ func (ev *evaluator) recover(expr parser.Expr, ws *storage.Warnings, errp *error } } -func (ev *evaluator) Eval(expr parser.Expr) (v parser.Value, ws storage.Warnings, err error) { +func (ev *evaluator) Eval(expr parser.Expr) (v parser.Value, ws annotations.Annotations, err error) { defer ev.recover(expr, &ws, &err) v, ws = ev.eval(expr) @@ -1109,19 +1110,19 @@ func (enh *EvalNodeHelper) DropMetricName(l labels.Labels) labels.Labels { // function call results. // The prepSeries function (if provided) can be used to prepare the helper // for each series, then passed to each call funcCall. -func (ev *evaluator) rangeEval(prepSeries func(labels.Labels, *EvalSeriesHelper), funcCall func([]parser.Value, [][]EvalSeriesHelper, *EvalNodeHelper) (Vector, storage.Warnings), exprs ...parser.Expr) (Matrix, storage.Warnings) { +func (ev *evaluator) rangeEval(prepSeries func(labels.Labels, *EvalSeriesHelper), funcCall func([]parser.Value, [][]EvalSeriesHelper, *EvalNodeHelper) (Vector, annotations.Annotations), exprs ...parser.Expr) (Matrix, annotations.Annotations) { numSteps := int((ev.endTimestamp-ev.startTimestamp)/ev.interval) + 1 matrixes := make([]Matrix, len(exprs)) origMatrixes := make([]Matrix, len(exprs)) originalNumSamples := ev.currentSamples - var warnings storage.Warnings + var warnings annotations.Annotations for i, e := range exprs { // Functions will take string arguments from the expressions, not the values. if e != nil && e.Type() != parser.ValueTypeString { // ev.currentSamples will be updated to the correct value within the ev.eval call. val, ws := ev.eval(e) - warnings = append(warnings, ws...) + warnings.Merge(ws) matrixes[i] = val.(Matrix) // Keep a copy of the original point slices so that they @@ -1233,7 +1234,7 @@ func (ev *evaluator) rangeEval(prepSeries func(labels.Labels, *EvalSeriesHelper) enh.Ts = ts result, ws := funcCall(args, bufHelpers, enh) enh.Out = result[:0] // Reuse result vector. - warnings = append(warnings, ws...) + warnings.Merge(ws) ev.currentSamples += len(result) // When we reset currentSamples to tempNumSamples during the next iteration of the loop it also @@ -1310,7 +1311,7 @@ func (ev *evaluator) rangeEval(prepSeries func(labels.Labels, *EvalSeriesHelper) // evalSubquery evaluates given SubqueryExpr and returns an equivalent // evaluated MatrixSelector in its place. Note that the Name and LabelMatchers are not set. -func (ev *evaluator) evalSubquery(subq *parser.SubqueryExpr) (*parser.MatrixSelector, int, storage.Warnings) { +func (ev *evaluator) evalSubquery(subq *parser.SubqueryExpr) (*parser.MatrixSelector, int, annotations.Annotations) { samplesStats := ev.samplesStats // Avoid double counting samples when running a subquery, those samples will be counted in later stage. ev.samplesStats = ev.samplesStats.NewChild() @@ -1343,7 +1344,7 @@ func (ev *evaluator) evalSubquery(subq *parser.SubqueryExpr) (*parser.MatrixSele } // eval evaluates the given expression as the given AST expression node requires. -func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) { +func (ev *evaluator) eval(expr parser.Expr) (parser.Value, annotations.Annotations) { // This is the top-level evaluation method. // Thus, we check for timeout/cancellation here. if err := contextDone(ev.ctx, "expression evaluation"); err != nil { @@ -1372,17 +1373,17 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) { param := unwrapStepInvariantExpr(e.Param) unwrapParenExpr(¶m) if s, ok := param.(*parser.StringLiteral); ok { - return ev.rangeEval(initSeries, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, storage.Warnings) { - return ev.aggregation(e.Op, sortedGrouping, e.Without, s.Val, v[0].(Vector), sh[0], enh), nil + return ev.rangeEval(initSeries, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return ev.aggregation(e, sortedGrouping, s.Val, v[0].(Vector), sh[0], enh) }, e.Expr) } - return ev.rangeEval(initSeries, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, storage.Warnings) { + return ev.rangeEval(initSeries, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { var param float64 if e.Param != nil { param = v[0].(Vector)[0].F } - return ev.aggregation(e.Op, sortedGrouping, e.Without, param, v[1].(Vector), sh[1], enh), nil + return ev.aggregation(e, sortedGrouping, param, v[1].(Vector), sh[1], enh) }, e.Param, e.Expr) case *parser.Call: @@ -1404,7 +1405,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) { var ( matrixArgIndex int matrixArg bool - warnings storage.Warnings + warnings annotations.Annotations ) for i := range e.Args { unwrapParenExpr(&e.Args[i]) @@ -1422,7 +1423,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) { // Replacing parser.SubqueryExpr with parser.MatrixSelector. val, totalSamples, ws := ev.evalSubquery(subq) e.Args[i] = val - warnings = append(warnings, ws...) + warnings.Merge(ws) defer func() { // subquery result takes space in the memory. Get rid of that at the end. val.VectorSelector.(*parser.VectorSelector).Series = nil @@ -1433,8 +1434,9 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) { } if !matrixArg { // Does not have a matrix argument. - return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, storage.Warnings) { - return call(v, e.Args, enh), warnings + return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + vec, annos := call(v, e.Args, enh) + return vec, warnings.Merge(annos) }, e.Args...) } @@ -1448,7 +1450,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) { otherArgs[i] = val.(Matrix) otherInArgs[i] = Vector{Sample{}} inArgs[i] = otherInArgs[i] - warnings = append(warnings, ws...) + warnings.Merge(ws) } } @@ -1459,7 +1461,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) { selVS := sel.VectorSelector.(*parser.VectorSelector) ws, err := checkAndExpandSeriesSet(ev.ctx, sel) - warnings = append(warnings, ws...) + warnings.Merge(ws) if err != nil { ev.error(errWithWarnings{fmt.Errorf("expanding series: %w", err), warnings}) } @@ -1522,8 +1524,10 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) { inMatrix[0].Histograms = histograms enh.Ts = ts // Make the function call. - outVec := call(inArgs, e.Args, enh) + outVec, annos := call(inArgs, e.Args, enh) + warnings.Merge(annos) ev.samplesStats.IncrementSamplesAtStep(step, int64(len(floats)+len(histograms))) + enh.Out = outVec[:0] if len(outVec) > 0 { if outVec[0].H == nil { @@ -1626,7 +1630,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) { case *parser.BinaryExpr: switch lt, rt := e.LHS.Type(), e.RHS.Type(); { case lt == parser.ValueTypeScalar && rt == parser.ValueTypeScalar: - return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, storage.Warnings) { + return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { val := scalarBinop(e.Op, v[0].(Vector)[0].F, v[1].(Vector)[0].F) return append(enh.Out, Sample{F: val}), nil }, e.LHS, e.RHS) @@ -1639,36 +1643,36 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) { } switch e.Op { case parser.LAND: - return ev.rangeEval(initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, storage.Warnings) { + return ev.rangeEval(initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return ev.VectorAnd(v[0].(Vector), v[1].(Vector), e.VectorMatching, sh[0], sh[1], enh), nil }, e.LHS, e.RHS) case parser.LOR: - return ev.rangeEval(initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, storage.Warnings) { + return ev.rangeEval(initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return ev.VectorOr(v[0].(Vector), v[1].(Vector), e.VectorMatching, sh[0], sh[1], enh), nil }, e.LHS, e.RHS) case parser.LUNLESS: - return ev.rangeEval(initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, storage.Warnings) { + return ev.rangeEval(initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return ev.VectorUnless(v[0].(Vector), v[1].(Vector), e.VectorMatching, sh[0], sh[1], enh), nil }, e.LHS, e.RHS) default: - return ev.rangeEval(initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, storage.Warnings) { + return ev.rangeEval(initSignatures, func(v []parser.Value, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return ev.VectorBinop(e.Op, v[0].(Vector), v[1].(Vector), e.VectorMatching, e.ReturnBool, sh[0], sh[1], enh), nil }, e.LHS, e.RHS) } case lt == parser.ValueTypeVector && rt == parser.ValueTypeScalar: - return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, storage.Warnings) { + return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return ev.VectorscalarBinop(e.Op, v[0].(Vector), Scalar{V: v[1].(Vector)[0].F}, false, e.ReturnBool, enh), nil }, e.LHS, e.RHS) case lt == parser.ValueTypeScalar && rt == parser.ValueTypeVector: - return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, storage.Warnings) { + return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return ev.VectorscalarBinop(e.Op, v[1].(Vector), Scalar{V: v[0].(Vector)[0].F}, true, e.ReturnBool, enh), nil }, e.LHS, e.RHS) } case *parser.NumberLiteral: - return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, storage.Warnings) { + return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return append(enh.Out, Sample{F: e.Val, Metric: labels.EmptyLabels()}), nil }) @@ -1834,7 +1838,7 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) { panic(fmt.Errorf("unhandled expression of type: %T", expr)) } -func (ev *evaluator) rangeEvalTimestampFunctionOverVectorSelector(vs *parser.VectorSelector, call FunctionCall, e *parser.Call) (parser.Value, storage.Warnings) { +func (ev *evaluator) rangeEvalTimestampFunctionOverVectorSelector(vs *parser.VectorSelector, call FunctionCall, e *parser.Call) (parser.Value, annotations.Annotations) { ws, err := checkAndExpandSeriesSet(ev.ctx, vs) if err != nil { ev.error(errWithWarnings{fmt.Errorf("expanding series: %w", err), ws}) @@ -1846,7 +1850,7 @@ func (ev *evaluator) rangeEvalTimestampFunctionOverVectorSelector(vs *parser.Vec seriesIterators[i] = storage.NewMemoizedIterator(it, durationMilliseconds(ev.lookbackDelta)) } - return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, storage.Warnings) { + return ev.rangeEval(nil, func(v []parser.Value, _ [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { if vs.Timestamp != nil { // This is a special case for "timestamp()" when the @ modifier is used, to ensure that // we return a point for each time step in this case. @@ -1874,7 +1878,8 @@ func (ev *evaluator) rangeEvalTimestampFunctionOverVectorSelector(vs *parser.Vec } } ev.samplesStats.UpdatePeak(ev.currentSamples) - return call([]parser.Value{vec}, e.Args, enh), ws + vec, annos := call([]parser.Value{vec}, e.Args, enh) + return vec, ws.Merge(annos) }) } @@ -1945,7 +1950,7 @@ func putHPointSlice(p []HPoint) { } // matrixSelector evaluates a *parser.MatrixSelector expression. -func (ev *evaluator) matrixSelector(node *parser.MatrixSelector) (Matrix, storage.Warnings) { +func (ev *evaluator) matrixSelector(node *parser.MatrixSelector) (Matrix, annotations.Annotations) { var ( vs = node.VectorSelector.(*parser.VectorSelector) @@ -2525,7 +2530,10 @@ type groupedAggregation struct { // aggregation evaluates an aggregation operation on a Vector. The provided grouping labels // must be sorted. -func (ev *evaluator) aggregation(op parser.ItemType, grouping []string, without bool, param interface{}, vec Vector, seriesHelper []EvalSeriesHelper, enh *EvalNodeHelper) Vector { +func (ev *evaluator) aggregation(e *parser.AggregateExpr, grouping []string, param interface{}, vec Vector, seriesHelper []EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + op := e.Op + without := e.Without + annos := annotations.Annotations{} result := map[uint64]*groupedAggregation{} orderedResult := []*groupedAggregation{} var k int64 @@ -2536,7 +2544,7 @@ func (ev *evaluator) aggregation(op parser.ItemType, grouping []string, without } k = int64(f) if k < 1 { - return Vector{} + return Vector{}, annos } } var q float64 @@ -2789,7 +2797,8 @@ func (ev *evaluator) aggregation(op parser.ItemType, grouping []string, without case parser.AVG: if aggr.hasFloat && aggr.hasHistogram { // We cannot aggregate histogram sample with a float64 sample. - // TODO(zenador): Issue warning when plumbing is in place. + metricName := aggr.labels.Get(labels.MetricName) + annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, e.Expr.PositionRange())) continue } if aggr.hasHistogram { @@ -2834,12 +2843,16 @@ func (ev *evaluator) aggregation(op parser.ItemType, grouping []string, without continue // Bypass default append. case parser.QUANTILE: + if math.IsNaN(q) || q < 0 || q > 1 { + annos.Add(annotations.NewInvalidQuantileWarning(q, e.Param.PositionRange())) + } aggr.floatValue = quantile(q, aggr.heap) case parser.SUM: if aggr.hasFloat && aggr.hasHistogram { // We cannot aggregate histogram sample with a float64 sample. - // TODO(zenador): Issue warning when plumbing is in place. + metricName := aggr.labels.Get(labels.MetricName) + annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, e.Expr.PositionRange())) continue } if aggr.hasHistogram { @@ -2855,7 +2868,7 @@ func (ev *evaluator) aggregation(op parser.ItemType, grouping []string, without H: aggr.histogramValue, }) } - return enh.Out + return enh.Out, annos } // groupingKey builds and returns the grouping key for the given metric and diff --git a/promql/engine_test.go b/promql/engine_test.go index ab97c9819..e0bb0b296 100644 --- a/promql/engine_test.go +++ b/promql/engine_test.go @@ -32,8 +32,10 @@ import ( "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/timestamp" "github.com/prometheus/prometheus/promql/parser" + "github.com/prometheus/prometheus/promql/parser/posrange" "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/tsdb/tsdbutil" + "github.com/prometheus/prometheus/util/annotations" "github.com/prometheus/prometheus/util/stats" "github.com/prometheus/prometheus/util/teststorage" ) @@ -198,11 +200,11 @@ func (q *errQuerier) Select(context.Context, bool, *storage.SelectHints, ...*lab return errSeriesSet{err: q.err} } -func (*errQuerier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, storage.Warnings, error) { +func (*errQuerier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, annotations.Annotations, error) { return nil, nil, nil } -func (*errQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, storage.Warnings, error) { +func (*errQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, annotations.Annotations, error) { return nil, nil, nil } func (*errQuerier) Close() error { return nil } @@ -212,10 +214,10 @@ type errSeriesSet struct { err error } -func (errSeriesSet) Next() bool { return false } -func (errSeriesSet) At() storage.Series { return nil } -func (e errSeriesSet) Err() error { return e.err } -func (e errSeriesSet) Warnings() storage.Warnings { return nil } +func (errSeriesSet) Next() bool { return false } +func (errSeriesSet) At() storage.Series { return nil } +func (e errSeriesSet) Err() error { return e.err } +func (e errSeriesSet) Warnings() annotations.Annotations { return nil } func TestQueryError(t *testing.T) { opts := EngineOpts{ @@ -1675,9 +1677,9 @@ func TestRecoverEvaluatorError(t *testing.T) { func TestRecoverEvaluatorErrorWithWarnings(t *testing.T) { ev := &evaluator{logger: log.NewNopLogger()} var err error - var ws storage.Warnings + var ws annotations.Annotations - warnings := storage.Warnings{errors.New("custom warning")} + warnings := annotations.New().Add(errors.New("custom warning")) e := errWithWarnings{ err: errors.New("custom error"), warnings: warnings, @@ -2146,7 +2148,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { expected: &parser.StepInvariantExpr{ Expr: &parser.NumberLiteral{ Val: 123.4567, - PosRange: parser.PositionRange{Start: 0, End: 8}, + PosRange: posrange.PositionRange{Start: 0, End: 8}, }, }, }, @@ -2155,7 +2157,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { expected: &parser.StepInvariantExpr{ Expr: &parser.StringLiteral{ Val: "foo", - PosRange: parser.PositionRange{Start: 0, End: 5}, + PosRange: posrange.PositionRange{Start: 0, End: 5}, }, }, }, @@ -2168,7 +2170,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "foo"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -2178,7 +2180,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "bar"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 6, End: 9, }, @@ -2195,7 +2197,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "foo"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -2206,7 +2208,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "bar"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 6, End: 14, }, @@ -2226,7 +2228,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "foo"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 8, }, @@ -2237,7 +2239,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "bar"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 11, End: 19, }, @@ -2255,7 +2257,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "test"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 4, }, @@ -2275,7 +2277,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { parser.MustLabelMatcher(labels.MatchEqual, "a", "b"), parser.MustLabelMatcher(labels.MatchEqual, "__name__", "test"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 11, }, @@ -2294,13 +2296,13 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "some_metric"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 13, End: 24, }, }, Grouping: []string{"foo"}, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 25, }, @@ -2316,14 +2318,14 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "some_metric"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 13, End: 29, }, Timestamp: makeInt64Pointer(10000), }, Grouping: []string{"foo"}, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 30, }, @@ -2343,13 +2345,13 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "some_metric1"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 4, End: 21, }, Timestamp: makeInt64Pointer(10000), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 22, }, @@ -2361,13 +2363,13 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "some_metric2"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 29, End: 46, }, Timestamp: makeInt64Pointer(20000), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 25, End: 47, }, @@ -2387,7 +2389,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "some_metric"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 11, }, @@ -2404,7 +2406,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "some_metric"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 29, End: 40, }, @@ -2414,19 +2416,19 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { EndPos: 49, }, }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 24, End: 50, }, }, Param: &parser.NumberLiteral{ Val: 5, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 21, End: 22, }, }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 16, End: 51, }, @@ -2439,7 +2441,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { expected: &parser.Call{ Func: parser.MustGetFunction("time"), Args: parser.Expressions{}, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 6, }, @@ -2454,7 +2456,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { parser.MustLabelMatcher(labels.MatchEqual, "bar", "baz"), parser.MustLabelMatcher(labels.MatchEqual, "__name__", "foo"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 14, }, @@ -2474,7 +2476,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { parser.MustLabelMatcher(labels.MatchEqual, "bar", "baz"), parser.MustLabelMatcher(labels.MatchEqual, "__name__", "foo"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 14, }, @@ -2499,13 +2501,13 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { parser.MustLabelMatcher(labels.MatchEqual, "bar", "baz"), parser.MustLabelMatcher(labels.MatchEqual, "__name__", "foo"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 4, End: 23, }, Timestamp: makeInt64Pointer(20000), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 24, }, @@ -2536,7 +2538,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { parser.MustLabelMatcher(labels.MatchEqual, "bar", "baz"), parser.MustLabelMatcher(labels.MatchEqual, "__name__", "foo"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 19, End: 33, }, @@ -2545,7 +2547,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { EndPos: 37, }, }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 14, End: 38, }, @@ -2555,7 +2557,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { EndPos: 56, }, }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 57, }, @@ -2575,7 +2577,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "some_metric"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 27, }, @@ -2597,7 +2599,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "some_metric"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 11, }, @@ -2625,7 +2627,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "foo"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 1, End: 4, }, @@ -2638,14 +2640,14 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { parser.MustLabelMatcher(labels.MatchEqual, "__name__", "bar"), }, Timestamp: makeInt64Pointer(1234000), - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 7, End: 27, }, }, }, }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 28, }, @@ -2676,18 +2678,18 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "metric"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 8, End: 19, }, Timestamp: makeInt64Pointer(10000), }}, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 4, End: 20, }, }}, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 21, }, @@ -2709,13 +2711,13 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "some_metric1"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 8, End: 25, }, Timestamp: makeInt64Pointer(10000), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 4, End: 26, }, @@ -2727,19 +2729,19 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "some_metric2"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 33, End: 50, }, Timestamp: makeInt64Pointer(20000), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 29, End: 52, }, }, }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 52, }, @@ -2754,7 +2756,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "foo"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 13, }, @@ -2771,7 +2773,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "foo"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 11, }, @@ -2791,7 +2793,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "test"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 4, }, @@ -2812,7 +2814,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "test"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 4, }, @@ -2831,7 +2833,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "some_metric"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 11, }, @@ -2853,7 +2855,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "some_metric"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 11, }, @@ -2883,7 +2885,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { LabelMatchers: []*labels.Matcher{ parser.MustLabelMatcher(labels.MatchEqual, "__name__", "some_metric"), }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 6, End: 17, }, @@ -2894,20 +2896,20 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { Op: parser.MUL, LHS: &parser.NumberLiteral{ Val: 3, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 21, End: 22, }, }, RHS: &parser.NumberLiteral{ Val: 1024, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 25, End: 29, }, }, }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 20, End: 30, }, @@ -2915,7 +2917,7 @@ func TestPreprocessAndWrapWithStepInvariantExpr(t *testing.T) { }, }, }, - PosRange: parser.PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 31, }, diff --git a/promql/functions.go b/promql/functions.go index 5c39d6bd8..b1245f5a1 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -28,6 +28,8 @@ import ( "github.com/prometheus/prometheus/model/histogram" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql/parser" + "github.com/prometheus/prometheus/promql/parser/posrange" + "github.com/prometheus/prometheus/util/annotations" ) // FunctionCall is the type of a PromQL function implementation @@ -51,20 +53,20 @@ import ( // metrics, the timestamp are not needed. // // Scalar results should be returned as the value of a sample in a Vector. -type FunctionCall func(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector +type FunctionCall func(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) // === time() float64 === -func funcTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +func funcTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return Vector{Sample{ F: float64(enh.Ts) / 1000, - }} + }}, nil } // extrapolatedRate is a utility function for rate/increase/delta. // It calculates the rate (allowing for counter resets if isCounter is true), // extrapolates if the first/last sample is close to the boundary, and returns // the result as either per-second (if isRate is true) or overall. -func extrapolatedRate(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper, isCounter, isRate bool) Vector { +func extrapolatedRate(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper, isCounter, isRate bool) (Vector, annotations.Annotations) { ms := args[0].(*parser.MatrixSelector) vs := ms.VectorSelector.(*parser.VectorSelector) var ( @@ -75,14 +77,19 @@ func extrapolatedRate(vals []parser.Value, args parser.Expressions, enh *EvalNod resultHistogram *histogram.FloatHistogram firstT, lastT int64 numSamplesMinusOne int + annos = annotations.Annotations{} ) // We need either at least two Histograms and no Floats, or at least two // Floats and no Histograms to calculate a rate. Otherwise, drop this // Vector element. + metricName := samples.Metric.Get(labels.MetricName) if len(samples.Histograms) > 0 && len(samples.Floats) > 0 { - // Mix of histograms and floats. TODO(beorn7): Communicate this failure reason. - return enh.Out + return enh.Out, annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args[0].PositionRange())) + } + + if isCounter && !strings.HasSuffix(metricName, "_total") && !strings.HasSuffix(metricName, "_sum") && !strings.HasSuffix(metricName, "_count") { + annos.Add(annotations.NewPossibleNonCounterInfo(metricName, args[0].PositionRange())) } switch { @@ -90,11 +97,11 @@ func extrapolatedRate(vals []parser.Value, args parser.Expressions, enh *EvalNod numSamplesMinusOne = len(samples.Histograms) - 1 firstT = samples.Histograms[0].T lastT = samples.Histograms[numSamplesMinusOne].T - resultHistogram = histogramRate(samples.Histograms, isCounter) + var newAnnos annotations.Annotations + resultHistogram, newAnnos = histogramRate(samples.Histograms, isCounter, metricName, args[0].PositionRange()) if resultHistogram == nil { // The histograms are not compatible with each other. - // TODO(beorn7): Communicate this failure reason. - return enh.Out + return enh.Out, annos.Merge(newAnnos) } case len(samples.Floats) > 1: numSamplesMinusOne = len(samples.Floats) - 1 @@ -113,8 +120,8 @@ func extrapolatedRate(vals []parser.Value, args parser.Expressions, enh *EvalNod prevValue = currPoint.F } default: - // Not enough samples. TODO(beorn7): Communicate this failure reason. - return enh.Out + // TODO: add RangeTooShortWarning + return enh.Out, annos } // Duration between first/last samples and boundary of range. @@ -165,17 +172,18 @@ func extrapolatedRate(vals []parser.Value, args parser.Expressions, enh *EvalNod resultHistogram.Mul(factor) } - return append(enh.Out, Sample{F: resultFloat, H: resultHistogram}) + return append(enh.Out, Sample{F: resultFloat, H: resultHistogram}), annos } // histogramRate is a helper function for extrapolatedRate. It requires // points[0] to be a histogram. It returns nil if any other Point in points is -// not a histogram. -func histogramRate(points []HPoint, isCounter bool) *histogram.FloatHistogram { +// not a histogram, and a warning wrapped in an annotation in that case. +// Otherwise, it returns the calculated histogram and an empty annotation. +func histogramRate(points []HPoint, isCounter bool, metricName string, pos posrange.PositionRange) (*histogram.FloatHistogram, annotations.Annotations) { prev := points[0].H last := points[len(points)-1].H if last == nil { - return nil // Range contains a mix of histograms and floats. + return nil, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(metricName, pos)) } minSchema := prev.Schema if last.Schema < minSchema { @@ -190,7 +198,7 @@ func histogramRate(points []HPoint, isCounter bool) *histogram.FloatHistogram { for _, currPoint := range points[1 : len(points)-1] { curr := currPoint.H if curr == nil { - return nil // Range contains a mix of histograms and floats. + return nil, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(metricName, pos)) } // TODO(trevorwhitney): Check if isCounter is consistent with curr.CounterResetHint. if !isCounter { @@ -216,40 +224,41 @@ func histogramRate(points []HPoint, isCounter bool) *histogram.FloatHistogram { } h.CounterResetHint = histogram.GaugeType - return h.Compact(0) + return h.Compact(0), nil } -// === delta(Matrix parser.ValueTypeMatrix) Vector === -func funcDelta(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === delta(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === +func funcDelta(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return extrapolatedRate(vals, args, enh, false, false) } -// === rate(node parser.ValueTypeMatrix) Vector === -func funcRate(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === rate(node parser.ValueTypeMatrix) (Vector, Annotations) === +func funcRate(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return extrapolatedRate(vals, args, enh, true, true) } -// === increase(node parser.ValueTypeMatrix) Vector === -func funcIncrease(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === increase(node parser.ValueTypeMatrix) (Vector, Annotations) === +func funcIncrease(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return extrapolatedRate(vals, args, enh, true, false) } -// === irate(node parser.ValueTypeMatrix) Vector === -func funcIrate(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === irate(node parser.ValueTypeMatrix) (Vector, Annotations) === +func funcIrate(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return instantValue(vals, enh.Out, true) } -// === idelta(node model.ValMatrix) Vector === -func funcIdelta(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === idelta(node model.ValMatrix) (Vector, Annotations) === +func funcIdelta(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return instantValue(vals, enh.Out, false) } -func instantValue(vals []parser.Value, out Vector, isRate bool) Vector { +func instantValue(vals []parser.Value, out Vector, isRate bool) (Vector, annotations.Annotations) { samples := vals[0].(Matrix)[0] // No sense in trying to compute a rate without at least two points. Drop // this Vector element. + // TODO: add RangeTooShortWarning if len(samples.Floats) < 2 { - return out + return out, nil } lastSample := samples.Floats[len(samples.Floats)-1] @@ -266,7 +275,7 @@ func instantValue(vals []parser.Value, out Vector, isRate bool) Vector { sampledInterval := lastSample.T - previousSample.T if sampledInterval == 0 { // Avoid dividing by 0. - return out + return out, nil } if isRate { @@ -274,7 +283,7 @@ func instantValue(vals []parser.Value, out Vector, isRate bool) Vector { resultValue /= float64(sampledInterval) / 1000 } - return append(out, Sample{F: resultValue}) + return append(out, Sample{F: resultValue}), nil } // Calculate the trend value at the given index i in raw data d. @@ -299,7 +308,7 @@ func calcTrendValue(i int, tf, s0, s1, b float64) float64 { // data. A lower smoothing factor increases the influence of historical data. The trend factor (0 < tf < 1) affects // how trends in historical data will affect the current data. A higher trend factor increases the influence. // of trends. Algorithm taken from https://en.wikipedia.org/wiki/Exponential_smoothing titled: "Double exponential smoothing". -func funcHoltWinters(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +func funcHoltWinters(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { samples := vals[0].(Matrix)[0] // The smoothing factor argument. @@ -320,7 +329,7 @@ func funcHoltWinters(vals []parser.Value, args parser.Expressions, enh *EvalNode // Can't do the smoothing operation with less than two points. if l < 2 { - return enh.Out + return enh.Out, nil } var s0, s1, b float64 @@ -342,34 +351,34 @@ func funcHoltWinters(vals []parser.Value, args parser.Expressions, enh *EvalNode s0, s1 = s1, x+y } - return append(enh.Out, Sample{F: s1}) + return append(enh.Out, Sample{F: s1}), nil } -// === sort(node parser.ValueTypeVector) Vector === -func funcSort(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === sort(node parser.ValueTypeVector) (Vector, Annotations) === +func funcSort(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { // NaN should sort to the bottom, so take descending sort with NaN first and // reverse it. byValueSorter := vectorByReverseValueHeap(vals[0].(Vector)) sort.Sort(sort.Reverse(byValueSorter)) - return Vector(byValueSorter) + return Vector(byValueSorter), nil } -// === sortDesc(node parser.ValueTypeVector) Vector === -func funcSortDesc(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === sortDesc(node parser.ValueTypeVector) (Vector, Annotations) === +func funcSortDesc(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { // NaN should sort to the bottom, so take ascending sort with NaN first and // reverse it. byValueSorter := vectorByValueHeap(vals[0].(Vector)) sort.Sort(sort.Reverse(byValueSorter)) - return Vector(byValueSorter) + return Vector(byValueSorter), nil } -// === clamp(Vector parser.ValueTypeVector, min, max Scalar) Vector === -func funcClamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === clamp(Vector parser.ValueTypeVector, min, max Scalar) (Vector, Annotations) === +func funcClamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { vec := vals[0].(Vector) min := vals[1].(Vector)[0].F max := vals[2].(Vector)[0].F if max < min { - return enh.Out + return enh.Out, nil } for _, el := range vec { enh.Out = append(enh.Out, Sample{ @@ -377,11 +386,11 @@ func funcClamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper F: math.Max(min, math.Min(max, el.F)), }) } - return enh.Out + return enh.Out, nil } -// === clamp_max(Vector parser.ValueTypeVector, max Scalar) Vector === -func funcClampMax(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === clamp_max(Vector parser.ValueTypeVector, max Scalar) (Vector, Annotations) === +func funcClampMax(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { vec := vals[0].(Vector) max := vals[1].(Vector)[0].F for _, el := range vec { @@ -390,11 +399,11 @@ func funcClampMax(vals []parser.Value, args parser.Expressions, enh *EvalNodeHel F: math.Min(max, el.F), }) } - return enh.Out + return enh.Out, nil } -// === clamp_min(Vector parser.ValueTypeVector, min Scalar) Vector === -func funcClampMin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === clamp_min(Vector parser.ValueTypeVector, min Scalar) (Vector, Annotations) === +func funcClampMin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { vec := vals[0].(Vector) min := vals[1].(Vector)[0].F for _, el := range vec { @@ -403,11 +412,11 @@ func funcClampMin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHel F: math.Max(min, el.F), }) } - return enh.Out + return enh.Out, nil } -// === round(Vector parser.ValueTypeVector, toNearest=1 Scalar) Vector === -func funcRound(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === round(Vector parser.ValueTypeVector, toNearest=1 Scalar) (Vector, Annotations) === +func funcRound(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { vec := vals[0].(Vector) // round returns a number rounded to toNearest. // Ties are solved by rounding up. @@ -425,16 +434,16 @@ func funcRound(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper F: f, }) } - return enh.Out + return enh.Out, nil } // === Scalar(node parser.ValueTypeVector) Scalar === -func funcScalar(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +func funcScalar(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { v := vals[0].(Vector) if len(v) != 1 { - return append(enh.Out, Sample{F: math.NaN()}) + return append(enh.Out, Sample{F: math.NaN()}), nil } - return append(enh.Out, Sample{F: v[0].F}) + return append(enh.Out, Sample{F: v[0].F}), nil } func aggrOverTime(vals []parser.Value, enh *EvalNodeHelper, aggrFn func(Series) float64) Vector { @@ -449,13 +458,14 @@ func aggrHistOverTime(vals []parser.Value, enh *EvalNodeHelper, aggrFn func(Seri return append(enh.Out, Sample{H: aggrFn(el)}) } -// === avg_over_time(Matrix parser.ValueTypeMatrix) Vector === -func funcAvgOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - if len(vals[0].(Matrix)[0].Floats) > 0 && len(vals[0].(Matrix)[0].Histograms) > 0 { - // TODO(zenador): Add warning for mixed floats and histograms. - return enh.Out +// === avg_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === +func funcAvgOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + firstSeries := vals[0].(Matrix)[0] + if len(firstSeries.Floats) > 0 && len(firstSeries.Histograms) > 0 { + metricName := firstSeries.Metric.Get(labels.MetricName) + return enh.Out, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args[0].PositionRange())) } - if len(vals[0].(Matrix)[0].Floats) == 0 { + if len(firstSeries.Floats) == 0 { // The passed values only contain histograms. return aggrHistOverTime(vals, enh, func(s Series) *histogram.FloatHistogram { count := 1 @@ -475,7 +485,7 @@ func funcAvgOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode } } return mean - }) + }), nil } return aggrOverTime(vals, enh, func(s Series) float64 { var mean, count, c float64 @@ -505,18 +515,18 @@ func funcAvgOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode return mean } return mean + c - }) + }), nil } -// === count_over_time(Matrix parser.ValueTypeMatrix) Vector === -func funcCountOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === count_over_time(Matrix parser.ValueTypeMatrix) (Vector, Notes) === +func funcCountOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return aggrOverTime(vals, enh, func(s Series) float64 { return float64(len(s.Floats) + len(s.Histograms)) - }) + }), nil } -// === last_over_time(Matrix parser.ValueTypeMatrix) Vector === -func funcLastOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === last_over_time(Matrix parser.ValueTypeMatrix) (Vector, Notes) === +func funcLastOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { el := vals[0].(Matrix)[0] var f FPoint @@ -533,22 +543,22 @@ func funcLastOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNod return append(enh.Out, Sample{ Metric: el.Metric, F: f.F, - }) + }), nil } return append(enh.Out, Sample{ Metric: el.Metric, H: h.H, - }) + }), nil } -// === max_over_time(Matrix parser.ValueTypeMatrix) Vector === -func funcMaxOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === max_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === +func funcMaxOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { if len(vals[0].(Matrix)[0].Floats) == 0 { // TODO(beorn7): The passed values only contain // histograms. max_over_time ignores histograms for now. If // there are only histograms, we have to return without adding // anything to enh.Out. - return enh.Out + return enh.Out, nil } return aggrOverTime(vals, enh, func(s Series) float64 { max := s.Floats[0].F @@ -558,17 +568,17 @@ func funcMaxOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode } } return max - }) + }), nil } -// === min_over_time(Matrix parser.ValueTypeMatrix) Vector === -func funcMinOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === min_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === +func funcMinOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { if len(vals[0].(Matrix)[0].Floats) == 0 { // TODO(beorn7): The passed values only contain // histograms. min_over_time ignores histograms for now. If // there are only histograms, we have to return without adding // anything to enh.Out. - return enh.Out + return enh.Out, nil } return aggrOverTime(vals, enh, func(s Series) float64 { min := s.Floats[0].F @@ -578,16 +588,17 @@ func funcMinOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode } } return min - }) + }), nil } -// === sum_over_time(Matrix parser.ValueTypeMatrix) Vector === -func funcSumOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - if len(vals[0].(Matrix)[0].Floats) > 0 && len(vals[0].(Matrix)[0].Histograms) > 0 { - // TODO(zenador): Add warning for mixed floats and histograms. - return enh.Out +// === sum_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === +func funcSumOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + firstSeries := vals[0].(Matrix)[0] + if len(firstSeries.Floats) > 0 && len(firstSeries.Histograms) > 0 { + metricName := firstSeries.Metric.Get(labels.MetricName) + return enh.Out, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args[0].PositionRange())) } - if len(vals[0].(Matrix)[0].Floats) == 0 { + if len(firstSeries.Floats) == 0 { // The passed values only contain histograms. return aggrHistOverTime(vals, enh, func(s Series) *histogram.FloatHistogram { sum := s.Histograms[0].H.Copy() @@ -601,7 +612,7 @@ func funcSumOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode } } return sum - }) + }), nil } return aggrOverTime(vals, enh, func(s Series) float64 { var sum, c float64 @@ -612,11 +623,11 @@ func funcSumOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNode return sum } return sum + c - }) + }), nil } -// === quantile_over_time(Matrix parser.ValueTypeMatrix) Vector === -func funcQuantileOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === quantile_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === +func funcQuantileOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { q := vals[0].(Vector)[0].F el := vals[1].(Matrix)[0] if len(el.Floats) == 0 { @@ -624,24 +635,29 @@ func funcQuantileOverTime(vals []parser.Value, args parser.Expressions, enh *Eva // histograms. quantile_over_time ignores histograms for now. If // there are only histograms, we have to return without adding // anything to enh.Out. - return enh.Out + return enh.Out, nil + } + + annos := annotations.Annotations{} + if math.IsNaN(q) || q < 0 || q > 1 { + annos.Add(annotations.NewInvalidQuantileWarning(q, args[0].PositionRange())) } values := make(vectorByValueHeap, 0, len(el.Floats)) for _, f := range el.Floats { values = append(values, Sample{F: f.F}) } - return append(enh.Out, Sample{F: quantile(q, values)}) + return append(enh.Out, Sample{F: quantile(q, values)}), annos } -// === stddev_over_time(Matrix parser.ValueTypeMatrix) Vector === -func funcStddevOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === stddev_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === +func funcStddevOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { if len(vals[0].(Matrix)[0].Floats) == 0 { // TODO(beorn7): The passed values only contain // histograms. stddev_over_time ignores histograms for now. If // there are only histograms, we have to return without adding // anything to enh.Out. - return enh.Out + return enh.Out, nil } return aggrOverTime(vals, enh, func(s Series) float64 { var count float64 @@ -654,17 +670,17 @@ func funcStddevOverTime(vals []parser.Value, args parser.Expressions, enh *EvalN aux, cAux = kahanSumInc(delta*(f.F-(mean+cMean)), aux, cAux) } return math.Sqrt((aux + cAux) / count) - }) + }), nil } -// === stdvar_over_time(Matrix parser.ValueTypeMatrix) Vector === -func funcStdvarOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === stdvar_over_time(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === +func funcStdvarOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { if len(vals[0].(Matrix)[0].Floats) == 0 { // TODO(beorn7): The passed values only contain // histograms. stdvar_over_time ignores histograms for now. If // there are only histograms, we have to return without adding // anything to enh.Out. - return enh.Out + return enh.Out, nil } return aggrOverTime(vals, enh, func(s Series) float64 { var count float64 @@ -677,35 +693,35 @@ func funcStdvarOverTime(vals []parser.Value, args parser.Expressions, enh *EvalN aux, cAux = kahanSumInc(delta*(f.F-(mean+cMean)), aux, cAux) } return (aux + cAux) / count - }) + }), nil } -// === absent(Vector parser.ValueTypeVector) Vector === -func funcAbsent(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === absent(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcAbsent(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { if len(vals[0].(Vector)) > 0 { - return enh.Out + return enh.Out, nil } return append(enh.Out, Sample{ Metric: createLabelsForAbsentFunction(args[0]), F: 1, - }) + }), nil } -// === absent_over_time(Vector parser.ValueTypeMatrix) Vector === +// === absent_over_time(Vector parser.ValueTypeMatrix) (Vector, Annotations) === // As this function has a matrix as argument, it does not get all the Series. // This function will return 1 if the matrix has at least one element. // Due to engine optimization, this function is only called when this condition is true. // Then, the engine post-processes the results to get the expected output. -func funcAbsentOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return append(enh.Out, Sample{F: 1}) +func funcAbsentOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return append(enh.Out, Sample{F: 1}), nil } -// === present_over_time(Vector parser.ValueTypeMatrix) Vector === -func funcPresentOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === present_over_time(Vector parser.ValueTypeMatrix) (Vector, Annotations) === +func funcPresentOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return aggrOverTime(vals, enh, func(s Series) float64 { return 1 - }) + }), nil } func simpleFunc(vals []parser.Value, enh *EvalNodeHelper, f func(float64) float64) Vector { @@ -720,127 +736,127 @@ func simpleFunc(vals []parser.Value, enh *EvalNodeHelper, f func(float64) float6 return enh.Out } -// === abs(Vector parser.ValueTypeVector) Vector === -func funcAbs(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Abs) +// === abs(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcAbs(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Abs), nil } -// === ceil(Vector parser.ValueTypeVector) Vector === -func funcCeil(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Ceil) +// === ceil(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcCeil(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Ceil), nil } -// === floor(Vector parser.ValueTypeVector) Vector === -func funcFloor(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Floor) +// === floor(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcFloor(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Floor), nil } -// === exp(Vector parser.ValueTypeVector) Vector === -func funcExp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Exp) +// === exp(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcExp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Exp), nil } -// === sqrt(Vector VectorNode) Vector === -func funcSqrt(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Sqrt) +// === sqrt(Vector VectorNode) (Vector, Annotations) === +func funcSqrt(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Sqrt), nil } -// === ln(Vector parser.ValueTypeVector) Vector === -func funcLn(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Log) +// === ln(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcLn(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Log), nil } -// === log2(Vector parser.ValueTypeVector) Vector === -func funcLog2(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Log2) +// === log2(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcLog2(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Log2), nil } -// === log10(Vector parser.ValueTypeVector) Vector === -func funcLog10(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Log10) +// === log10(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcLog10(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Log10), nil } -// === sin(Vector parser.ValueTypeVector) Vector === -func funcSin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Sin) +// === sin(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcSin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Sin), nil } -// === cos(Vector parser.ValueTypeVector) Vector === -func funcCos(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Cos) +// === cos(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcCos(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Cos), nil } -// === tan(Vector parser.ValueTypeVector) Vector === -func funcTan(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Tan) +// === tan(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcTan(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Tan), nil } -// == asin(Vector parser.ValueTypeVector) Vector === -func funcAsin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Asin) +// == asin(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcAsin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Asin), nil } -// == acos(Vector parser.ValueTypeVector) Vector === -func funcAcos(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Acos) +// == acos(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcAcos(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Acos), nil } -// == atan(Vector parser.ValueTypeVector) Vector === -func funcAtan(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Atan) +// == atan(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcAtan(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Atan), nil } -// == sinh(Vector parser.ValueTypeVector) Vector === -func funcSinh(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Sinh) +// == sinh(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcSinh(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Sinh), nil } -// == cosh(Vector parser.ValueTypeVector) Vector === -func funcCosh(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Cosh) +// == cosh(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcCosh(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Cosh), nil } -// == tanh(Vector parser.ValueTypeVector) Vector === -func funcTanh(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Tanh) +// == tanh(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcTanh(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Tanh), nil } -// == asinh(Vector parser.ValueTypeVector) Vector === -func funcAsinh(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Asinh) +// == asinh(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcAsinh(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Asinh), nil } -// == acosh(Vector parser.ValueTypeVector) Vector === -func funcAcosh(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Acosh) +// == acosh(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcAcosh(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Acosh), nil } -// == atanh(Vector parser.ValueTypeVector) Vector === -func funcAtanh(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return simpleFunc(vals, enh, math.Atanh) +// == atanh(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcAtanh(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return simpleFunc(vals, enh, math.Atanh), nil } -// === rad(Vector parser.ValueTypeVector) Vector === -func funcRad(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === rad(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcRad(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return simpleFunc(vals, enh, func(v float64) float64 { return v * math.Pi / 180 - }) + }), nil } -// === deg(Vector parser.ValueTypeVector) Vector === -func funcDeg(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === deg(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcDeg(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return simpleFunc(vals, enh, func(v float64) float64 { return v * 180 / math.Pi - }) + }), nil } // === pi() Scalar === -func funcPi(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { - return Vector{Sample{F: math.Pi}} +func funcPi(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return Vector{Sample{F: math.Pi}}, nil } -// === sgn(Vector parser.ValueTypeVector) Vector === -func funcSgn(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === sgn(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcSgn(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return simpleFunc(vals, enh, func(v float64) float64 { switch { case v < 0: @@ -850,11 +866,11 @@ func funcSgn(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) default: return v } - }) + }), nil } -// === timestamp(Vector parser.ValueTypeVector) Vector === -func funcTimestamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === timestamp(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcTimestamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { vec := vals[0].(Vector) for _, el := range vec { enh.Out = append(enh.Out, Sample{ @@ -862,7 +878,7 @@ func funcTimestamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHe F: float64(el.T) / 1000, }) } - return enh.Out + return enh.Out, nil } func kahanSum(samples []float64) float64 { @@ -931,39 +947,39 @@ func linearRegression(samples []FPoint, interceptTime int64) (slope, intercept f return slope, intercept } -// === deriv(node parser.ValueTypeMatrix) Vector === -func funcDeriv(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === deriv(node parser.ValueTypeMatrix) (Vector, Annotations) === +func funcDeriv(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { samples := vals[0].(Matrix)[0] // No sense in trying to compute a derivative without at least two points. // Drop this Vector element. if len(samples.Floats) < 2 { - return enh.Out + return enh.Out, nil } // We pass in an arbitrary timestamp that is near the values in use // to avoid floating point accuracy issues, see // https://github.com/prometheus/prometheus/issues/2674 slope, _ := linearRegression(samples.Floats, samples.Floats[0].T) - return append(enh.Out, Sample{F: slope}) + return append(enh.Out, Sample{F: slope}), nil } -// === predict_linear(node parser.ValueTypeMatrix, k parser.ValueTypeScalar) Vector === -func funcPredictLinear(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === predict_linear(node parser.ValueTypeMatrix, k parser.ValueTypeScalar) (Vector, Annotations) === +func funcPredictLinear(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { samples := vals[0].(Matrix)[0] duration := vals[1].(Vector)[0].F // No sense in trying to predict anything without at least two points. // Drop this Vector element. if len(samples.Floats) < 2 { - return enh.Out + return enh.Out, nil } slope, intercept := linearRegression(samples.Floats, enh.Ts) - return append(enh.Out, Sample{F: slope*duration + intercept}) + return append(enh.Out, Sample{F: slope*duration + intercept}), nil } -// === histogram_count(Vector parser.ValueTypeVector) Vector === -func funcHistogramCount(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === histogram_count(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcHistogramCount(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { inVec := vals[0].(Vector) for _, sample := range inVec { @@ -976,11 +992,11 @@ func funcHistogramCount(vals []parser.Value, args parser.Expressions, enh *EvalN F: sample.H.Count, }) } - return enh.Out + return enh.Out, nil } -// === histogram_sum(Vector parser.ValueTypeVector) Vector === -func funcHistogramSum(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === histogram_sum(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcHistogramSum(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { inVec := vals[0].(Vector) for _, sample := range inVec { @@ -993,11 +1009,11 @@ func funcHistogramSum(vals []parser.Value, args parser.Expressions, enh *EvalNod F: sample.H.Sum, }) } - return enh.Out + return enh.Out, nil } -// === histogram_stddev(Vector parser.ValueTypeVector) Vector === -func funcHistogramStdDev(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === histogram_stddev(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcHistogramStdDev(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { inVec := vals[0].(Vector) for _, sample := range inVec { @@ -1026,11 +1042,11 @@ func funcHistogramStdDev(vals []parser.Value, args parser.Expressions, enh *Eval F: math.Sqrt(variance), }) } - return enh.Out + return enh.Out, nil } -// === histogram_stdvar(Vector parser.ValueTypeVector) Vector === -func funcHistogramStdVar(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === histogram_stdvar(Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcHistogramStdVar(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { inVec := vals[0].(Vector) for _, sample := range inVec { @@ -1059,11 +1075,11 @@ func funcHistogramStdVar(vals []parser.Value, args parser.Expressions, enh *Eval F: variance, }) } - return enh.Out + return enh.Out, nil } -// === histogram_fraction(lower, upper parser.ValueTypeScalar, Vector parser.ValueTypeVector) Vector === -func funcHistogramFraction(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === histogram_fraction(lower, upper parser.ValueTypeScalar, Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcHistogramFraction(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { lower := vals[0].(Vector)[0].F upper := vals[1].(Vector)[0].F inVec := vals[2].(Vector) @@ -1078,13 +1094,18 @@ func funcHistogramFraction(vals []parser.Value, args parser.Expressions, enh *Ev F: histogramFraction(lower, upper, sample.H), }) } - return enh.Out + return enh.Out, nil } -// === histogram_quantile(k parser.ValueTypeScalar, Vector parser.ValueTypeVector) Vector === -func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === histogram_quantile(k parser.ValueTypeScalar, Vector parser.ValueTypeVector) (Vector, Annotations) === +func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { q := vals[0].(Vector)[0].F inVec := vals[1].(Vector) + annos := annotations.Annotations{} + + if math.IsNaN(q) || q < 0 || q > 1 { + annos.Add(annotations.NewInvalidQuantileWarning(q, args[0].PositionRange())) + } if enh.signatureToMetricWithBuckets == nil { enh.signatureToMetricWithBuckets = map[string]*metricWithBuckets{} @@ -1108,8 +1129,7 @@ func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *Ev sample.Metric.Get(model.BucketLabel), 64, ) if err != nil { - // Oops, no bucket label or malformed label value. Skip. - // TODO(beorn7): Issue a warning somehow. + annos.Add(annotations.NewBadBucketLabelWarning(sample.Metric.Get(labels.MetricName), sample.Metric.Get(model.BucketLabel), args[1].PositionRange())) continue } enh.lblBuf = sample.Metric.BytesWithoutLabels(enh.lblBuf, labels.BucketLabel) @@ -1135,7 +1155,7 @@ func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *Ev // At this data point, we have conventional histogram // buckets and a native histogram with the same name and // labels. Do not evaluate anything. - // TODO(beorn7): Issue a warning somehow. + annos.Add(annotations.NewMixedClassicNativeHistogramsWarning(sample.Metric.Get(labels.MetricName), args[1].PositionRange())) delete(enh.signatureToMetricWithBuckets, string(enh.lblBuf)) continue } @@ -1155,11 +1175,11 @@ func funcHistogramQuantile(vals []parser.Value, args parser.Expressions, enh *Ev } } - return enh.Out + return enh.Out, annos } -// === resets(Matrix parser.ValueTypeMatrix) Vector === -func funcResets(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === resets(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === +func funcResets(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { floats := vals[0].(Matrix)[0].Floats histograms := vals[0].(Matrix)[0].Histograms resets := 0 @@ -1186,17 +1206,17 @@ func funcResets(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelpe } } - return append(enh.Out, Sample{F: float64(resets)}) + return append(enh.Out, Sample{F: float64(resets)}), nil } -// === changes(Matrix parser.ValueTypeMatrix) Vector === -func funcChanges(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === changes(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === +func funcChanges(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { floats := vals[0].(Matrix)[0].Floats changes := 0 if len(floats) == 0 { // TODO(beorn7): Only histogram values, still need to add support. - return enh.Out + return enh.Out, nil } prev := floats[0].F @@ -1208,11 +1228,11 @@ func funcChanges(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelp prev = current } - return append(enh.Out, Sample{F: float64(changes)}) + return append(enh.Out, Sample{F: float64(changes)}), nil } -// === label_replace(Vector parser.ValueTypeVector, dst_label, replacement, src_labelname, regex parser.ValueTypeString) Vector === -func funcLabelReplace(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === label_replace(Vector parser.ValueTypeVector, dst_label, replacement, src_labelname, regex parser.ValueTypeString) (Vector, Annotations) === +func funcLabelReplace(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { var ( vector = vals[0].(Vector) dst = stringFromArg(args[1]) @@ -1263,20 +1283,20 @@ func funcLabelReplace(vals []parser.Value, args parser.Expressions, enh *EvalNod H: el.H, }) } - return enh.Out + return enh.Out, nil } -// === Vector(s Scalar) Vector === -func funcVector(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === Vector(s Scalar) (Vector, Annotations) === +func funcVector(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return append(enh.Out, Sample{ Metric: labels.Labels{}, F: vals[0].(Vector)[0].F, - }) + }), nil } -// === label_join(vector model.ValVector, dest_labelname, separator, src_labelname...) Vector === -func funcLabelJoin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +// === label_join(vector model.ValVector, dest_labelname, separator, src_labelname...) (Vector, Annotations) === +func funcLabelJoin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { var ( vector = vals[0].(Vector) dst = stringFromArg(args[1]) @@ -1331,7 +1351,7 @@ func funcLabelJoin(vals []parser.Value, args parser.Expressions, enh *EvalNodeHe H: el.H, }) } - return enh.Out + return enh.Out, nil } // Common code for date related functions. @@ -1355,59 +1375,59 @@ func dateWrapper(vals []parser.Value, enh *EvalNodeHelper, f func(time.Time) flo } // === days_in_month(v Vector) Scalar === -func funcDaysInMonth(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +func funcDaysInMonth(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return dateWrapper(vals, enh, func(t time.Time) float64 { return float64(32 - time.Date(t.Year(), t.Month(), 32, 0, 0, 0, 0, time.UTC).Day()) - }) + }), nil } // === day_of_month(v Vector) Scalar === -func funcDayOfMonth(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +func funcDayOfMonth(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return dateWrapper(vals, enh, func(t time.Time) float64 { return float64(t.Day()) - }) + }), nil } // === day_of_week(v Vector) Scalar === -func funcDayOfWeek(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +func funcDayOfWeek(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return dateWrapper(vals, enh, func(t time.Time) float64 { return float64(t.Weekday()) - }) + }), nil } // === day_of_year(v Vector) Scalar === -func funcDayOfYear(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +func funcDayOfYear(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return dateWrapper(vals, enh, func(t time.Time) float64 { return float64(t.YearDay()) - }) + }), nil } // === hour(v Vector) Scalar === -func funcHour(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +func funcHour(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return dateWrapper(vals, enh, func(t time.Time) float64 { return float64(t.Hour()) - }) + }), nil } // === minute(v Vector) Scalar === -func funcMinute(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +func funcMinute(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return dateWrapper(vals, enh, func(t time.Time) float64 { return float64(t.Minute()) - }) + }), nil } // === month(v Vector) Scalar === -func funcMonth(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +func funcMonth(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return dateWrapper(vals, enh, func(t time.Time) float64 { return float64(t.Month()) - }) + }), nil } // === year(v Vector) Scalar === -func funcYear(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { +func funcYear(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return dateWrapper(vals, enh, func(t time.Time) float64 { return float64(t.Year()) - }) + }), nil } // FunctionCalls is a list of all functions supported by PromQL, including their types. diff --git a/promql/parser/ast.go b/promql/parser/ast.go index 86f139499..58136266f 100644 --- a/promql/parser/ast.go +++ b/promql/parser/ast.go @@ -20,6 +20,8 @@ import ( "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/storage" + + "github.com/prometheus/prometheus/promql/parser/posrange" ) // Node is a generic interface for all nodes in an AST. @@ -45,7 +47,7 @@ type Node interface { Pretty(level int) string // PositionRange returns the position of the AST Node in the query string. - PositionRange() PositionRange + PositionRange() posrange.PositionRange } // Statement is a generic interface for all statements. @@ -94,7 +96,7 @@ type AggregateExpr struct { Param Expr // Parameter used by some aggregators. Grouping []string // The labels by which to group the Vector. Without bool // Whether to drop the given labels rather than keep them. - PosRange PositionRange + PosRange posrange.PositionRange } // BinaryExpr represents a binary expression between two child expressions. @@ -115,7 +117,7 @@ type Call struct { Func *Function // The function that was called. Args Expressions // Arguments used in the call. - PosRange PositionRange + PosRange posrange.PositionRange } // MatrixSelector represents a Matrix selection. @@ -125,7 +127,7 @@ type MatrixSelector struct { VectorSelector Expr Range time.Duration - EndPos Pos + EndPos posrange.Pos } // SubqueryExpr represents a subquery. @@ -143,27 +145,27 @@ type SubqueryExpr struct { StartOrEnd ItemType // Set when @ is used with start() or end() Step time.Duration - EndPos Pos + EndPos posrange.Pos } // NumberLiteral represents a number. type NumberLiteral struct { Val float64 - PosRange PositionRange + PosRange posrange.PositionRange } // ParenExpr wraps an expression so it cannot be disassembled as a consequence // of operator precedence. type ParenExpr struct { Expr Expr - PosRange PositionRange + PosRange posrange.PositionRange } // StringLiteral represents a string. type StringLiteral struct { Val string - PosRange PositionRange + PosRange posrange.PositionRange } // UnaryExpr represents a unary operation on another expression. @@ -172,7 +174,7 @@ type UnaryExpr struct { Op ItemType Expr Expr - StartPos Pos + StartPos posrange.Pos } // StepInvariantExpr represents a query which evaluates to the same result @@ -184,7 +186,9 @@ type StepInvariantExpr struct { func (e *StepInvariantExpr) String() string { return e.Expr.String() } -func (e *StepInvariantExpr) PositionRange() PositionRange { return e.Expr.PositionRange() } +func (e *StepInvariantExpr) PositionRange() posrange.PositionRange { + return e.Expr.PositionRange() +} // VectorSelector represents a Vector selection. type VectorSelector struct { @@ -204,7 +208,7 @@ type VectorSelector struct { UnexpandedSeriesSet storage.SeriesSet Series []storage.Series - PosRange PositionRange + PosRange posrange.PositionRange } // TestStmt is an internal helper statement that allows execution @@ -215,8 +219,8 @@ func (TestStmt) String() string { return "test statement" } func (TestStmt) PromQLStmt() {} func (t TestStmt) Pretty(int) string { return t.String() } -func (TestStmt) PositionRange() PositionRange { - return PositionRange{ +func (TestStmt) PositionRange() posrange.PositionRange { + return posrange.PositionRange{ Start: -1, End: -1, } @@ -405,17 +409,11 @@ func Children(node Node) []Node { } } -// PositionRange describes a position in the input string of the parser. -type PositionRange struct { - Start Pos - End Pos -} - // mergeRanges is a helper function to merge the PositionRanges of two Nodes. // Note that the arguments must be in the same order as they // occur in the input string. -func mergeRanges(first, last Node) PositionRange { - return PositionRange{ +func mergeRanges(first, last Node) posrange.PositionRange { + return posrange.PositionRange{ Start: first.PositionRange().Start, End: last.PositionRange().End, } @@ -423,33 +421,33 @@ func mergeRanges(first, last Node) PositionRange { // Item implements the Node interface. // This makes it possible to call mergeRanges on them. -func (i *Item) PositionRange() PositionRange { - return PositionRange{ +func (i *Item) PositionRange() posrange.PositionRange { + return posrange.PositionRange{ Start: i.Pos, - End: i.Pos + Pos(len(i.Val)), + End: i.Pos + posrange.Pos(len(i.Val)), } } -func (e *AggregateExpr) PositionRange() PositionRange { +func (e *AggregateExpr) PositionRange() posrange.PositionRange { return e.PosRange } -func (e *BinaryExpr) PositionRange() PositionRange { +func (e *BinaryExpr) PositionRange() posrange.PositionRange { return mergeRanges(e.LHS, e.RHS) } -func (e *Call) PositionRange() PositionRange { +func (e *Call) PositionRange() posrange.PositionRange { return e.PosRange } -func (e *EvalStmt) PositionRange() PositionRange { +func (e *EvalStmt) PositionRange() posrange.PositionRange { return e.Expr.PositionRange() } -func (e Expressions) PositionRange() PositionRange { +func (e Expressions) PositionRange() posrange.PositionRange { if len(e) == 0 { // Position undefined. - return PositionRange{ + return posrange.PositionRange{ Start: -1, End: -1, } @@ -457,39 +455,39 @@ func (e Expressions) PositionRange() PositionRange { return mergeRanges(e[0], e[len(e)-1]) } -func (e *MatrixSelector) PositionRange() PositionRange { - return PositionRange{ +func (e *MatrixSelector) PositionRange() posrange.PositionRange { + return posrange.PositionRange{ Start: e.VectorSelector.PositionRange().Start, End: e.EndPos, } } -func (e *SubqueryExpr) PositionRange() PositionRange { - return PositionRange{ +func (e *SubqueryExpr) PositionRange() posrange.PositionRange { + return posrange.PositionRange{ Start: e.Expr.PositionRange().Start, End: e.EndPos, } } -func (e *NumberLiteral) PositionRange() PositionRange { +func (e *NumberLiteral) PositionRange() posrange.PositionRange { return e.PosRange } -func (e *ParenExpr) PositionRange() PositionRange { +func (e *ParenExpr) PositionRange() posrange.PositionRange { return e.PosRange } -func (e *StringLiteral) PositionRange() PositionRange { +func (e *StringLiteral) PositionRange() posrange.PositionRange { return e.PosRange } -func (e *UnaryExpr) PositionRange() PositionRange { - return PositionRange{ +func (e *UnaryExpr) PositionRange() posrange.PositionRange { + return posrange.PositionRange{ Start: e.StartPos, End: e.Expr.PositionRange().End, } } -func (e *VectorSelector) PositionRange() PositionRange { +func (e *VectorSelector) PositionRange() posrange.PositionRange { return e.PosRange } diff --git a/promql/parser/generated_parser.y b/promql/parser/generated_parser.y index f7951db2b..676fd9fb5 100644 --- a/promql/parser/generated_parser.y +++ b/promql/parser/generated_parser.y @@ -22,6 +22,7 @@ import ( "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/value" "github.com/prometheus/prometheus/model/histogram" + "github.com/prometheus/prometheus/promql/parser/posrange" ) %} @@ -199,7 +200,7 @@ start : { yylex.(*parser).generatedParserResult = $2 } | START_SERIES_DESCRIPTION series_description | START_EXPRESSION /* empty */ EOF - { yylex.(*parser).addParseErrf(PositionRange{}, "no expression found in input")} + { yylex.(*parser).addParseErrf(posrange.PositionRange{}, "no expression found in input")} | START_EXPRESSION expr { yylex.(*parser).generatedParserResult = $2 } | START_METRIC_SELECTOR vector_selector @@ -371,7 +372,7 @@ function_call : IDENTIFIER function_call_body $$ = &Call{ Func: fn, Args: $2.(Expressions), - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: $1.Pos, End: yylex.(*parser).lastClosing, }, diff --git a/promql/parser/generated_parser.y.go b/promql/parser/generated_parser.y.go index d7fc9081b..77a403be3 100644 --- a/promql/parser/generated_parser.y.go +++ b/promql/parser/generated_parser.y.go @@ -15,9 +15,10 @@ import ( "github.com/prometheus/prometheus/model/histogram" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/value" + "github.com/prometheus/prometheus/promql/parser/posrange" ) -//line promql/parser/generated_parser.y:29 +//line promql/parser/generated_parser.y:30 type yySymType struct { yys int node Node @@ -229,7 +230,7 @@ const yyEofCode = 1 const yyErrCode = 2 const yyInitialStackSize = 16 -//line promql/parser/generated_parser.y:915 +//line promql/parser/generated_parser.y:916 //line yacctab:1 var yyExca = [...]int16{ @@ -994,62 +995,62 @@ yydefault: case 1: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:199 +//line promql/parser/generated_parser.y:200 { yylex.(*parser).generatedParserResult = yyDollar[2].labels } case 3: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:202 +//line promql/parser/generated_parser.y:203 { - yylex.(*parser).addParseErrf(PositionRange{}, "no expression found in input") + yylex.(*parser).addParseErrf(posrange.PositionRange{}, "no expression found in input") } case 4: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:204 +//line promql/parser/generated_parser.y:205 { yylex.(*parser).generatedParserResult = yyDollar[2].node } case 5: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:206 +//line promql/parser/generated_parser.y:207 { yylex.(*parser).generatedParserResult = yyDollar[2].node } case 7: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:209 +//line promql/parser/generated_parser.y:210 { yylex.(*parser).unexpected("", "") } case 20: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:232 +//line promql/parser/generated_parser.y:233 { yyVAL.node = yylex.(*parser).newAggregateExpr(yyDollar[1].item, yyDollar[2].node, yyDollar[3].node) } case 21: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:234 +//line promql/parser/generated_parser.y:235 { yyVAL.node = yylex.(*parser).newAggregateExpr(yyDollar[1].item, yyDollar[3].node, yyDollar[2].node) } case 22: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:236 +//line promql/parser/generated_parser.y:237 { yyVAL.node = yylex.(*parser).newAggregateExpr(yyDollar[1].item, &AggregateExpr{}, yyDollar[2].node) } case 23: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:238 +//line promql/parser/generated_parser.y:239 { yylex.(*parser).unexpected("aggregation", "") yyVAL.node = yylex.(*parser).newAggregateExpr(yyDollar[1].item, &AggregateExpr{}, Expressions{}) } case 24: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:246 +//line promql/parser/generated_parser.y:247 { yyVAL.node = &AggregateExpr{ Grouping: yyDollar[2].strings, @@ -1057,7 +1058,7 @@ yydefault: } case 25: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:252 +//line promql/parser/generated_parser.y:253 { yyVAL.node = &AggregateExpr{ Grouping: yyDollar[2].strings, @@ -1066,103 +1067,103 @@ yydefault: } case 26: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:265 +//line promql/parser/generated_parser.y:266 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 27: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:266 +//line promql/parser/generated_parser.y:267 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 28: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:267 +//line promql/parser/generated_parser.y:268 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 29: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:268 +//line promql/parser/generated_parser.y:269 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 30: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:269 +//line promql/parser/generated_parser.y:270 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 31: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:270 +//line promql/parser/generated_parser.y:271 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 32: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:271 +//line promql/parser/generated_parser.y:272 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 33: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:272 +//line promql/parser/generated_parser.y:273 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 34: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:273 +//line promql/parser/generated_parser.y:274 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 35: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:274 +//line promql/parser/generated_parser.y:275 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 36: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:275 +//line promql/parser/generated_parser.y:276 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 37: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:276 +//line promql/parser/generated_parser.y:277 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 38: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:277 +//line promql/parser/generated_parser.y:278 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 39: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:278 +//line promql/parser/generated_parser.y:279 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 40: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:279 +//line promql/parser/generated_parser.y:280 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 41: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:280 +//line promql/parser/generated_parser.y:281 { yyVAL.node = yylex.(*parser).newBinaryExpression(yyDollar[1].node, yyDollar[2].item, yyDollar[3].node, yyDollar[4].node) } case 43: yyDollar = yyS[yypt-0 : yypt+1] -//line promql/parser/generated_parser.y:288 +//line promql/parser/generated_parser.y:289 { yyVAL.node = &BinaryExpr{ VectorMatching: &VectorMatching{Card: CardOneToOne}, @@ -1170,7 +1171,7 @@ yydefault: } case 44: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:293 +//line promql/parser/generated_parser.y:294 { yyVAL.node = &BinaryExpr{ VectorMatching: &VectorMatching{Card: CardOneToOne}, @@ -1179,14 +1180,14 @@ yydefault: } case 45: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:301 +//line promql/parser/generated_parser.y:302 { yyVAL.node = yyDollar[1].node yyVAL.node.(*BinaryExpr).VectorMatching.MatchingLabels = yyDollar[3].strings } case 46: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:306 +//line promql/parser/generated_parser.y:307 { yyVAL.node = yyDollar[1].node yyVAL.node.(*BinaryExpr).VectorMatching.MatchingLabels = yyDollar[3].strings @@ -1194,7 +1195,7 @@ yydefault: } case 49: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:316 +//line promql/parser/generated_parser.y:317 { yyVAL.node = yyDollar[1].node yyVAL.node.(*BinaryExpr).VectorMatching.Card = CardManyToOne @@ -1202,7 +1203,7 @@ yydefault: } case 50: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:322 +//line promql/parser/generated_parser.y:323 { yyVAL.node = yyDollar[1].node yyVAL.node.(*BinaryExpr).VectorMatching.Card = CardOneToMany @@ -1210,51 +1211,51 @@ yydefault: } case 51: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:331 +//line promql/parser/generated_parser.y:332 { yyVAL.strings = yyDollar[2].strings } case 52: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:333 +//line promql/parser/generated_parser.y:334 { yyVAL.strings = yyDollar[2].strings } case 53: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:335 +//line promql/parser/generated_parser.y:336 { yyVAL.strings = []string{} } case 54: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:337 +//line promql/parser/generated_parser.y:338 { yylex.(*parser).unexpected("grouping opts", "\"(\"") yyVAL.strings = nil } case 55: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:343 +//line promql/parser/generated_parser.y:344 { yyVAL.strings = append(yyDollar[1].strings, yyDollar[3].item.Val) } case 56: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:345 +//line promql/parser/generated_parser.y:346 { yyVAL.strings = []string{yyDollar[1].item.Val} } case 57: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:347 +//line promql/parser/generated_parser.y:348 { yylex.(*parser).unexpected("grouping opts", "\",\" or \")\"") yyVAL.strings = yyDollar[1].strings } case 58: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:351 +//line promql/parser/generated_parser.y:352 { if !isLabel(yyDollar[1].item.Val) { yylex.(*parser).unexpected("grouping opts", "label") @@ -1263,14 +1264,14 @@ yydefault: } case 59: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:358 +//line promql/parser/generated_parser.y:359 { yylex.(*parser).unexpected("grouping opts", "label") yyVAL.item = Item{} } case 60: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:366 +//line promql/parser/generated_parser.y:367 { fn, exist := getFunction(yyDollar[1].item.Val, yylex.(*parser).functions) if !exist { @@ -1279,7 +1280,7 @@ yydefault: yyVAL.node = &Call{ Func: fn, Args: yyDollar[2].node.(Expressions), - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: yyDollar[1].item.Pos, End: yylex.(*parser).lastClosing, }, @@ -1287,86 +1288,86 @@ yydefault: } case 61: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:383 +//line promql/parser/generated_parser.y:384 { yyVAL.node = yyDollar[2].node } case 62: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:385 +//line promql/parser/generated_parser.y:386 { yyVAL.node = Expressions{} } case 63: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:389 +//line promql/parser/generated_parser.y:390 { yyVAL.node = append(yyDollar[1].node.(Expressions), yyDollar[3].node.(Expr)) } case 64: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:391 +//line promql/parser/generated_parser.y:392 { yyVAL.node = Expressions{yyDollar[1].node.(Expr)} } case 65: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:393 +//line promql/parser/generated_parser.y:394 { yylex.(*parser).addParseErrf(yyDollar[2].item.PositionRange(), "trailing commas not allowed in function call args") yyVAL.node = yyDollar[1].node } case 66: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:404 +//line promql/parser/generated_parser.y:405 { yyVAL.node = &ParenExpr{Expr: yyDollar[2].node.(Expr), PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[3].item)} } case 67: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:412 +//line promql/parser/generated_parser.y:413 { yylex.(*parser).addOffset(yyDollar[1].node, yyDollar[3].duration) yyVAL.node = yyDollar[1].node } case 68: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:417 +//line promql/parser/generated_parser.y:418 { yylex.(*parser).addOffset(yyDollar[1].node, -yyDollar[4].duration) yyVAL.node = yyDollar[1].node } case 69: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:422 +//line promql/parser/generated_parser.y:423 { yylex.(*parser).unexpected("offset", "duration") yyVAL.node = yyDollar[1].node } case 70: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:429 +//line promql/parser/generated_parser.y:430 { yylex.(*parser).setTimestamp(yyDollar[1].node, yyDollar[3].float) yyVAL.node = yyDollar[1].node } case 71: yyDollar = yyS[yypt-5 : yypt+1] -//line promql/parser/generated_parser.y:434 +//line promql/parser/generated_parser.y:435 { yylex.(*parser).setAtModifierPreprocessor(yyDollar[1].node, yyDollar[3].item) yyVAL.node = yyDollar[1].node } case 72: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:439 +//line promql/parser/generated_parser.y:440 { yylex.(*parser).unexpected("@", "timestamp") yyVAL.node = yyDollar[1].node } case 75: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:449 +//line promql/parser/generated_parser.y:450 { var errMsg string vs, ok := yyDollar[1].node.(*VectorSelector) @@ -1391,7 +1392,7 @@ yydefault: } case 76: yyDollar = yyS[yypt-6 : yypt+1] -//line promql/parser/generated_parser.y:474 +//line promql/parser/generated_parser.y:475 { yyVAL.node = &SubqueryExpr{ Expr: yyDollar[1].node.(Expr), @@ -1403,35 +1404,35 @@ yydefault: } case 77: yyDollar = yyS[yypt-6 : yypt+1] -//line promql/parser/generated_parser.y:484 +//line promql/parser/generated_parser.y:485 { yylex.(*parser).unexpected("subquery selector", "\"]\"") yyVAL.node = yyDollar[1].node } case 78: yyDollar = yyS[yypt-5 : yypt+1] -//line promql/parser/generated_parser.y:486 +//line promql/parser/generated_parser.y:487 { yylex.(*parser).unexpected("subquery selector", "duration or \"]\"") yyVAL.node = yyDollar[1].node } case 79: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:488 +//line promql/parser/generated_parser.y:489 { yylex.(*parser).unexpected("subquery or range", "\":\" or \"]\"") yyVAL.node = yyDollar[1].node } case 80: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:490 +//line promql/parser/generated_parser.y:491 { yylex.(*parser).unexpected("subquery selector", "duration") yyVAL.node = yyDollar[1].node } case 81: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:500 +//line promql/parser/generated_parser.y:501 { if nl, ok := yyDollar[2].node.(*NumberLiteral); ok { if yyDollar[1].item.Typ == SUB { @@ -1445,7 +1446,7 @@ yydefault: } case 82: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:518 +//line promql/parser/generated_parser.y:519 { vs := yyDollar[2].node.(*VectorSelector) vs.PosRange = mergeRanges(&yyDollar[1].item, vs) @@ -1455,7 +1456,7 @@ yydefault: } case 83: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:526 +//line promql/parser/generated_parser.y:527 { vs := &VectorSelector{ Name: yyDollar[1].item.Val, @@ -1467,7 +1468,7 @@ yydefault: } case 84: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:536 +//line promql/parser/generated_parser.y:537 { vs := yyDollar[1].node.(*VectorSelector) yylex.(*parser).assembleVectorSelector(vs) @@ -1475,7 +1476,7 @@ yydefault: } case 85: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:544 +//line promql/parser/generated_parser.y:545 { yyVAL.node = &VectorSelector{ LabelMatchers: yyDollar[2].matchers, @@ -1484,7 +1485,7 @@ yydefault: } case 86: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:551 +//line promql/parser/generated_parser.y:552 { yyVAL.node = &VectorSelector{ LabelMatchers: yyDollar[2].matchers, @@ -1493,7 +1494,7 @@ yydefault: } case 87: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:558 +//line promql/parser/generated_parser.y:559 { yyVAL.node = &VectorSelector{ LabelMatchers: []*labels.Matcher{}, @@ -1502,7 +1503,7 @@ yydefault: } case 88: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:567 +//line promql/parser/generated_parser.y:568 { if yyDollar[1].matchers != nil { yyVAL.matchers = append(yyDollar[1].matchers, yyDollar[3].matcher) @@ -1512,47 +1513,47 @@ yydefault: } case 89: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:575 +//line promql/parser/generated_parser.y:576 { yyVAL.matchers = []*labels.Matcher{yyDollar[1].matcher} } case 90: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:577 +//line promql/parser/generated_parser.y:578 { yylex.(*parser).unexpected("label matching", "\",\" or \"}\"") yyVAL.matchers = yyDollar[1].matchers } case 91: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:581 +//line promql/parser/generated_parser.y:582 { yyVAL.matcher = yylex.(*parser).newLabelMatcher(yyDollar[1].item, yyDollar[2].item, yyDollar[3].item) } case 92: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:583 +//line promql/parser/generated_parser.y:584 { yylex.(*parser).unexpected("label matching", "string") yyVAL.matcher = nil } case 93: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:585 +//line promql/parser/generated_parser.y:586 { yylex.(*parser).unexpected("label matching", "label matching operator") yyVAL.matcher = nil } case 94: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:587 +//line promql/parser/generated_parser.y:588 { yylex.(*parser).unexpected("label matching", "identifier or \"}\"") yyVAL.matcher = nil } case 95: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:595 +//line promql/parser/generated_parser.y:596 { b := labels.NewBuilder(yyDollar[2].labels) b.Set(labels.MetricName, yyDollar[1].item.Val) @@ -1560,83 +1561,83 @@ yydefault: } case 96: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:597 +//line promql/parser/generated_parser.y:598 { yyVAL.labels = yyDollar[1].labels } case 119: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:604 +//line promql/parser/generated_parser.y:605 { yyVAL.labels = labels.New(yyDollar[2].lblList...) } case 120: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:606 +//line promql/parser/generated_parser.y:607 { yyVAL.labels = labels.New(yyDollar[2].lblList...) } case 121: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:608 +//line promql/parser/generated_parser.y:609 { yyVAL.labels = labels.New() } case 122: yyDollar = yyS[yypt-0 : yypt+1] -//line promql/parser/generated_parser.y:610 +//line promql/parser/generated_parser.y:611 { yyVAL.labels = labels.New() } case 123: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:614 +//line promql/parser/generated_parser.y:615 { yyVAL.lblList = append(yyDollar[1].lblList, yyDollar[3].label) } case 124: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:616 +//line promql/parser/generated_parser.y:617 { yyVAL.lblList = []labels.Label{yyDollar[1].label} } case 125: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:618 +//line promql/parser/generated_parser.y:619 { yylex.(*parser).unexpected("label set", "\",\" or \"}\"") yyVAL.lblList = yyDollar[1].lblList } case 126: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:623 +//line promql/parser/generated_parser.y:624 { yyVAL.label = labels.Label{Name: yyDollar[1].item.Val, Value: yylex.(*parser).unquoteString(yyDollar[3].item.Val)} } case 127: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:625 +//line promql/parser/generated_parser.y:626 { yylex.(*parser).unexpected("label set", "string") yyVAL.label = labels.Label{} } case 128: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:627 +//line promql/parser/generated_parser.y:628 { yylex.(*parser).unexpected("label set", "\"=\"") yyVAL.label = labels.Label{} } case 129: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:629 +//line promql/parser/generated_parser.y:630 { yylex.(*parser).unexpected("label set", "identifier or \"}\"") yyVAL.label = labels.Label{} } case 130: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:640 +//line promql/parser/generated_parser.y:641 { yylex.(*parser).generatedParserResult = &seriesDescription{ labels: yyDollar[1].labels, @@ -1645,38 +1646,38 @@ yydefault: } case 131: yyDollar = yyS[yypt-0 : yypt+1] -//line promql/parser/generated_parser.y:649 +//line promql/parser/generated_parser.y:650 { yyVAL.series = []SequenceValue{} } case 132: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:651 +//line promql/parser/generated_parser.y:652 { yyVAL.series = append(yyDollar[1].series, yyDollar[3].series...) } case 133: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:653 +//line promql/parser/generated_parser.y:654 { yyVAL.series = yyDollar[1].series } case 134: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:655 +//line promql/parser/generated_parser.y:656 { yylex.(*parser).unexpected("series values", "") yyVAL.series = nil } case 135: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:659 +//line promql/parser/generated_parser.y:660 { yyVAL.series = []SequenceValue{{Omitted: true}} } case 136: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:661 +//line promql/parser/generated_parser.y:662 { yyVAL.series = []SequenceValue{} for i := uint64(0); i < yyDollar[3].uint; i++ { @@ -1685,13 +1686,13 @@ yydefault: } case 137: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:668 +//line promql/parser/generated_parser.y:669 { yyVAL.series = []SequenceValue{{Value: yyDollar[1].float}} } case 138: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:670 +//line promql/parser/generated_parser.y:671 { yyVAL.series = []SequenceValue{} // Add an additional value for time 0, which we ignore in tests. @@ -1701,7 +1702,7 @@ yydefault: } case 139: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:678 +//line promql/parser/generated_parser.y:679 { yyVAL.series = []SequenceValue{} // Add an additional value for time 0, which we ignore in tests. @@ -1712,13 +1713,13 @@ yydefault: } case 140: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:688 +//line promql/parser/generated_parser.y:689 { yyVAL.series = []SequenceValue{{Histogram: yyDollar[1].histogram}} } case 141: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:692 +//line promql/parser/generated_parser.y:693 { yyVAL.series = []SequenceValue{} // Add an additional value for time 0, which we ignore in tests. @@ -1729,7 +1730,7 @@ yydefault: } case 142: yyDollar = yyS[yypt-5 : yypt+1] -//line promql/parser/generated_parser.y:701 +//line promql/parser/generated_parser.y:702 { val, err := yylex.(*parser).histogramsIncreaseSeries(yyDollar[1].histogram, yyDollar[3].histogram, yyDollar[5].uint) if err != nil { @@ -1739,7 +1740,7 @@ yydefault: } case 143: yyDollar = yyS[yypt-5 : yypt+1] -//line promql/parser/generated_parser.y:709 +//line promql/parser/generated_parser.y:710 { val, err := yylex.(*parser).histogramsDecreaseSeries(yyDollar[1].histogram, yyDollar[3].histogram, yyDollar[5].uint) if err != nil { @@ -1749,7 +1750,7 @@ yydefault: } case 144: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:719 +//line promql/parser/generated_parser.y:720 { if yyDollar[1].item.Val != "stale" { yylex.(*parser).unexpected("series values", "number or \"stale\"") @@ -1758,138 +1759,138 @@ yydefault: } case 147: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:731 +//line promql/parser/generated_parser.y:732 { yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&yyDollar[2].descriptors) } case 148: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:735 +//line promql/parser/generated_parser.y:736 { yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&yyDollar[2].descriptors) } case 149: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:739 +//line promql/parser/generated_parser.y:740 { m := yylex.(*parser).newMap() yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&m) } case 150: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:744 +//line promql/parser/generated_parser.y:745 { m := yylex.(*parser).newMap() yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&m) } case 151: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:752 +//line promql/parser/generated_parser.y:753 { yyVAL.descriptors = *(yylex.(*parser).mergeMaps(&yyDollar[1].descriptors, &yyDollar[3].descriptors)) } case 152: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:756 +//line promql/parser/generated_parser.y:757 { yyVAL.descriptors = yyDollar[1].descriptors } case 153: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:759 +//line promql/parser/generated_parser.y:760 { yylex.(*parser).unexpected("histogram description", "histogram description key, e.g. buckets:[5 10 7]") } case 154: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:766 +//line promql/parser/generated_parser.y:767 { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["schema"] = yyDollar[3].int } case 155: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:771 +//line promql/parser/generated_parser.y:772 { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["sum"] = yyDollar[3].float } case 156: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:776 +//line promql/parser/generated_parser.y:777 { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["count"] = yyDollar[3].float } case 157: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:781 +//line promql/parser/generated_parser.y:782 { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["z_bucket"] = yyDollar[3].float } case 158: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:786 +//line promql/parser/generated_parser.y:787 { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["z_bucket_w"] = yyDollar[3].float } case 159: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:791 +//line promql/parser/generated_parser.y:792 { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["buckets"] = yyDollar[3].bucket_set } case 160: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:796 +//line promql/parser/generated_parser.y:797 { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["offset"] = yyDollar[3].int } case 161: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:801 +//line promql/parser/generated_parser.y:802 { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["n_buckets"] = yyDollar[3].bucket_set } case 162: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:806 +//line promql/parser/generated_parser.y:807 { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["n_offset"] = yyDollar[3].int } case 163: yyDollar = yyS[yypt-4 : yypt+1] -//line promql/parser/generated_parser.y:813 +//line promql/parser/generated_parser.y:814 { yyVAL.bucket_set = yyDollar[2].bucket_set } case 164: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:817 +//line promql/parser/generated_parser.y:818 { yyVAL.bucket_set = yyDollar[2].bucket_set } case 165: yyDollar = yyS[yypt-3 : yypt+1] -//line promql/parser/generated_parser.y:823 +//line promql/parser/generated_parser.y:824 { yyVAL.bucket_set = append(yyDollar[1].bucket_set, yyDollar[3].float) } case 166: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:827 +//line promql/parser/generated_parser.y:828 { yyVAL.bucket_set = []float64{yyDollar[1].float} } case 213: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:852 +//line promql/parser/generated_parser.y:853 { yyVAL.node = &NumberLiteral{ Val: yylex.(*parser).number(yyDollar[1].item.Val), @@ -1898,25 +1899,25 @@ yydefault: } case 214: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:860 +//line promql/parser/generated_parser.y:861 { yyVAL.float = yylex.(*parser).number(yyDollar[1].item.Val) } case 215: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:862 +//line promql/parser/generated_parser.y:863 { yyVAL.float = yyDollar[2].float } case 216: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:863 +//line promql/parser/generated_parser.y:864 { yyVAL.float = -yyDollar[2].float } case 219: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:869 +//line promql/parser/generated_parser.y:870 { var err error yyVAL.uint, err = strconv.ParseUint(yyDollar[1].item.Val, 10, 64) @@ -1926,19 +1927,19 @@ yydefault: } case 220: yyDollar = yyS[yypt-2 : yypt+1] -//line promql/parser/generated_parser.y:878 +//line promql/parser/generated_parser.y:879 { yyVAL.int = -int64(yyDollar[2].uint) } case 221: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:879 +//line promql/parser/generated_parser.y:880 { yyVAL.int = int64(yyDollar[1].uint) } case 222: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:883 +//line promql/parser/generated_parser.y:884 { var err error yyVAL.duration, err = parseDuration(yyDollar[1].item.Val) @@ -1948,7 +1949,7 @@ yydefault: } case 223: yyDollar = yyS[yypt-1 : yypt+1] -//line promql/parser/generated_parser.y:894 +//line promql/parser/generated_parser.y:895 { yyVAL.node = &StringLiteral{ Val: yylex.(*parser).unquoteString(yyDollar[1].item.Val), @@ -1957,13 +1958,13 @@ yydefault: } case 224: yyDollar = yyS[yypt-0 : yypt+1] -//line promql/parser/generated_parser.y:907 +//line promql/parser/generated_parser.y:908 { yyVAL.duration = 0 } case 226: yyDollar = yyS[yypt-0 : yypt+1] -//line promql/parser/generated_parser.y:911 +//line promql/parser/generated_parser.y:912 { yyVAL.strings = nil } diff --git a/promql/parser/lex.go b/promql/parser/lex.go index 4f5e735cb..c8bfcc2e1 100644 --- a/promql/parser/lex.go +++ b/promql/parser/lex.go @@ -19,13 +19,15 @@ import ( "strings" "unicode" "unicode/utf8" + + "github.com/prometheus/prometheus/promql/parser/posrange" ) // Item represents a token or text string returned from the scanner. type Item struct { - Typ ItemType // The type of this Item. - Pos Pos // The starting position, in bytes, of this Item in the input string. - Val string // The value of this Item. + Typ ItemType // The type of this Item. + Pos posrange.Pos // The starting position, in bytes, of this Item in the input string. + Val string // The value of this Item. } // String returns a descriptive string for the Item. @@ -234,10 +236,6 @@ const eof = -1 // stateFn represents the state of the scanner as a function that returns the next state. type stateFn func(*Lexer) stateFn -// Pos is the position in a string. -// Negative numbers indicate undefined positions. -type Pos int - type histogramState int const ( @@ -250,14 +248,14 @@ const ( // Lexer holds the state of the scanner. type Lexer struct { - input string // The string being scanned. - state stateFn // The next lexing function to enter. - pos Pos // Current position in the input. - start Pos // Start position of this Item. - width Pos // Width of last rune read from input. - lastPos Pos // Position of most recent Item returned by NextItem. - itemp *Item // Pointer to where the next scanned item should be placed. - scannedItem bool // Set to true every time an item is scanned. + input string // The string being scanned. + state stateFn // The next lexing function to enter. + pos posrange.Pos // Current position in the input. + start posrange.Pos // Start position of this Item. + width posrange.Pos // Width of last rune read from input. + lastPos posrange.Pos // Position of most recent Item returned by NextItem. + itemp *Item // Pointer to where the next scanned item should be placed. + scannedItem bool // Set to true every time an item is scanned. parenDepth int // Nesting depth of ( ) exprs. braceOpen bool // Whether a { is opened. @@ -278,7 +276,7 @@ func (l *Lexer) next() rune { return eof } r, w := utf8.DecodeRuneInString(l.input[l.pos:]) - l.width = Pos(w) + l.width = posrange.Pos(w) l.pos += l.width return r } @@ -827,7 +825,7 @@ func lexSpace(l *Lexer) stateFn { // lexLineComment scans a line comment. Left comment marker is known to be present. func lexLineComment(l *Lexer) stateFn { - l.pos += Pos(len(lineComment)) + l.pos += posrange.Pos(len(lineComment)) for r := l.next(); !isEndOfLine(r) && r != eof; { r = l.next() } diff --git a/promql/parser/lex_test.go b/promql/parser/lex_test.go index 237b71ec8..23c9dfbee 100644 --- a/promql/parser/lex_test.go +++ b/promql/parser/lex_test.go @@ -17,6 +17,8 @@ import ( "testing" "github.com/stretchr/testify/require" + + "github.com/prometheus/prometheus/promql/parser/posrange" ) type testCase struct { @@ -824,7 +826,7 @@ func TestLexer(t *testing.T) { require.Fail(t, "unexpected lexing error at position %d: %s", lastItem.Pos, lastItem) } - eofItem := Item{EOF, Pos(len(test.input)), ""} + eofItem := Item{EOF, posrange.Pos(len(test.input)), ""} require.Equal(t, lastItem, eofItem, "%d: input %q", i, test.input) out = out[:len(out)-1] diff --git a/promql/parser/parse.go b/promql/parser/parse.go index 05ff22f86..34217697a 100644 --- a/promql/parser/parse.go +++ b/promql/parser/parse.go @@ -29,6 +29,7 @@ import ( "github.com/prometheus/prometheus/model/histogram" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/timestamp" + "github.com/prometheus/prometheus/promql/parser/posrange" "github.com/prometheus/prometheus/util/strutil" ) @@ -54,7 +55,7 @@ type parser struct { // Everytime an Item is lexed that could be the end // of certain expressions its end position is stored here. - lastClosing Pos + lastClosing posrange.Pos yyParser yyParserImpl @@ -121,7 +122,7 @@ func (p *parser) Close() { // ParseErr wraps a parsing error with line and position context. type ParseErr struct { - PositionRange PositionRange + PositionRange posrange.PositionRange Err error Query string @@ -130,27 +131,7 @@ type ParseErr struct { } func (e *ParseErr) Error() string { - pos := int(e.PositionRange.Start) - lastLineBreak := -1 - line := e.LineOffset + 1 - - var positionStr string - - if pos < 0 || pos > len(e.Query) { - positionStr = "invalid position:" - } else { - - for i, c := range e.Query[:pos] { - if c == '\n' { - lastLineBreak = i - line++ - } - } - - col := pos - lastLineBreak - positionStr = fmt.Sprintf("%d:%d:", line, col) - } - return fmt.Sprintf("%s parse error: %s", positionStr, e.Err) + return fmt.Sprintf("%s: parse error: %s", e.PositionRange.StartPosInput(e.Query, e.LineOffset), e.Err) } type ParseErrors []ParseErr @@ -275,12 +256,12 @@ func ParseSeriesDesc(input string) (labels labels.Labels, values []SequenceValue } // addParseErrf formats the error and appends it to the list of parsing errors. -func (p *parser) addParseErrf(positionRange PositionRange, format string, args ...interface{}) { +func (p *parser) addParseErrf(positionRange posrange.PositionRange, format string, args ...interface{}) { p.addParseErr(positionRange, fmt.Errorf(format, args...)) } // addParseErr appends the provided error to the list of parsing errors. -func (p *parser) addParseErr(positionRange PositionRange, err error) { +func (p *parser) addParseErr(positionRange posrange.PositionRange, err error) { perr := ParseErr{ PositionRange: positionRange, Err: err, @@ -366,9 +347,9 @@ func (p *parser) Lex(lval *yySymType) int { switch typ { case ERROR: - pos := PositionRange{ + pos := posrange.PositionRange{ Start: p.lex.start, - End: Pos(len(p.lex.input)), + End: posrange.Pos(len(p.lex.input)), } p.addParseErr(pos, errors.New(p.yyParser.lval.item.Val)) @@ -378,7 +359,7 @@ func (p *parser) Lex(lval *yySymType) int { lval.item.Typ = EOF p.InjectItem(0) case RIGHT_BRACE, RIGHT_PAREN, RIGHT_BRACKET, DURATION, NUMBER: - p.lastClosing = lval.item.Pos + Pos(len(lval.item.Val)) + p.lastClosing = lval.item.Pos + posrange.Pos(len(lval.item.Val)) } return int(typ) @@ -436,7 +417,7 @@ func (p *parser) newAggregateExpr(op Item, modifier, args Node) (ret *AggregateE ret = modifier.(*AggregateExpr) arguments := args.(Expressions) - ret.PosRange = PositionRange{ + ret.PosRange = posrange.PositionRange{ Start: op.Pos, End: p.lastClosing, } @@ -477,7 +458,7 @@ func (p *parser) newMap() (ret map[string]interface{}) { func (p *parser) mergeMaps(left, right *map[string]interface{}) (ret *map[string]interface{}) { for key, value := range *right { if _, ok := (*left)[key]; ok { - p.addParseErrf(PositionRange{}, "duplicate key \"%s\" in histogram", key) + p.addParseErrf(posrange.PositionRange{}, "duplicate key \"%s\" in histogram", key) continue } (*left)[key] = value @@ -677,7 +658,7 @@ func (p *parser) checkAST(node Node) (typ ValueType) { // opRange returns the PositionRange of the operator part of the BinaryExpr. // This is made a function instead of a variable, so it is lazily evaluated on demand. - opRange := func() (r PositionRange) { + opRange := func() (r posrange.PositionRange) { // Remove whitespace at the beginning and end of the range. for r.Start = n.LHS.PositionRange().End; isSpace(rune(p.lex.input[r.Start])); r.Start++ { // nolint:revive } @@ -881,7 +862,7 @@ func (p *parser) newLabelMatcher(label, operator, value Item) *labels.Matcher { // addOffset is used to set the offset in the generated parser. func (p *parser) addOffset(e Node, offset time.Duration) { var orgoffsetp *time.Duration - var endPosp *Pos + var endPosp *posrange.Pos switch s := e.(type) { case *VectorSelector: @@ -921,7 +902,7 @@ func (p *parser) setTimestamp(e Node, ts float64) { p.addParseErrf(e.PositionRange(), "timestamp out of bounds for @ modifier: %f", ts) } var timestampp **int64 - var endPosp *Pos + var endPosp *posrange.Pos timestampp, _, endPosp, ok := p.getAtModifierVars(e) if !ok { @@ -950,11 +931,11 @@ func (p *parser) setAtModifierPreprocessor(e Node, op Item) { *endPosp = p.lastClosing } -func (p *parser) getAtModifierVars(e Node) (**int64, *ItemType, *Pos, bool) { +func (p *parser) getAtModifierVars(e Node) (**int64, *ItemType, *posrange.Pos, bool) { var ( timestampp **int64 preprocp *ItemType - endPosp *Pos + endPosp *posrange.Pos ) switch s := e.(type) { case *VectorSelector: diff --git a/promql/parser/parse_test.go b/promql/parser/parse_test.go index 82fdbbb90..2a7936b45 100644 --- a/promql/parser/parse_test.go +++ b/promql/parser/parse_test.go @@ -26,6 +26,8 @@ import ( "github.com/prometheus/prometheus/model/histogram" "github.com/prometheus/prometheus/model/labels" + + "github.com/prometheus/prometheus/promql/parser/posrange" ) var testExpr = []struct { @@ -39,84 +41,84 @@ var testExpr = []struct { input: "1", expected: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, }, { input: "+Inf", expected: &NumberLiteral{ Val: math.Inf(1), - PosRange: PositionRange{Start: 0, End: 4}, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, }, { input: "-Inf", expected: &NumberLiteral{ Val: math.Inf(-1), - PosRange: PositionRange{Start: 0, End: 4}, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, }, { input: ".5", expected: &NumberLiteral{ Val: 0.5, - PosRange: PositionRange{Start: 0, End: 2}, + PosRange: posrange.PositionRange{Start: 0, End: 2}, }, }, { input: "5.", expected: &NumberLiteral{ Val: 5, - PosRange: PositionRange{Start: 0, End: 2}, + PosRange: posrange.PositionRange{Start: 0, End: 2}, }, }, { input: "123.4567", expected: &NumberLiteral{ Val: 123.4567, - PosRange: PositionRange{Start: 0, End: 8}, + PosRange: posrange.PositionRange{Start: 0, End: 8}, }, }, { input: "5e-3", expected: &NumberLiteral{ Val: 0.005, - PosRange: PositionRange{Start: 0, End: 4}, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, }, { input: "5e3", expected: &NumberLiteral{ Val: 5000, - PosRange: PositionRange{Start: 0, End: 3}, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, }, { input: "0xc", expected: &NumberLiteral{ Val: 12, - PosRange: PositionRange{Start: 0, End: 3}, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, }, { input: "0755", expected: &NumberLiteral{ Val: 493, - PosRange: PositionRange{Start: 0, End: 4}, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, }, { input: "+5.5e-3", expected: &NumberLiteral{ Val: 0.0055, - PosRange: PositionRange{Start: 0, End: 7}, + PosRange: posrange.PositionRange{Start: 0, End: 7}, }, }, { input: "-0755", expected: &NumberLiteral{ Val: -493, - PosRange: PositionRange{Start: 0, End: 5}, + PosRange: posrange.PositionRange{Start: 0, End: 5}, }, }, { @@ -125,11 +127,11 @@ var testExpr = []struct { Op: ADD, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 4, End: 5}, + PosRange: posrange.PositionRange{Start: 4, End: 5}, }, }, }, @@ -139,11 +141,11 @@ var testExpr = []struct { Op: SUB, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 4, End: 5}, + PosRange: posrange.PositionRange{Start: 4, End: 5}, }, }, }, @@ -153,11 +155,11 @@ var testExpr = []struct { Op: MUL, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 4, End: 5}, + PosRange: posrange.PositionRange{Start: 4, End: 5}, }, }, }, @@ -167,11 +169,11 @@ var testExpr = []struct { Op: MOD, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 4, End: 5}, + PosRange: posrange.PositionRange{Start: 4, End: 5}, }, }, }, @@ -181,11 +183,11 @@ var testExpr = []struct { Op: DIV, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 4, End: 5}, + PosRange: posrange.PositionRange{Start: 4, End: 5}, }, }, }, @@ -195,11 +197,11 @@ var testExpr = []struct { Op: EQLC, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 10, End: 11}, + PosRange: posrange.PositionRange{Start: 10, End: 11}, }, ReturnBool: true, }, @@ -210,11 +212,11 @@ var testExpr = []struct { Op: NEQ, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 10, End: 11}, + PosRange: posrange.PositionRange{Start: 10, End: 11}, }, ReturnBool: true, }, @@ -225,11 +227,11 @@ var testExpr = []struct { Op: GTR, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 9, End: 10}, + PosRange: posrange.PositionRange{Start: 9, End: 10}, }, ReturnBool: true, }, @@ -240,11 +242,11 @@ var testExpr = []struct { Op: GTE, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 10, End: 11}, + PosRange: posrange.PositionRange{Start: 10, End: 11}, }, ReturnBool: true, }, @@ -255,11 +257,11 @@ var testExpr = []struct { Op: LSS, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 9, End: 10}, + PosRange: posrange.PositionRange{Start: 9, End: 10}, }, ReturnBool: true, }, @@ -270,11 +272,11 @@ var testExpr = []struct { Op: LTE, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 10, End: 11}, + PosRange: posrange.PositionRange{Start: 10, End: 11}, }, ReturnBool: true, }, @@ -287,11 +289,11 @@ var testExpr = []struct { Op: POW, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 1, End: 2}, + PosRange: posrange.PositionRange{Start: 1, End: 2}, }, RHS: &NumberLiteral{ Val: 2, - PosRange: PositionRange{Start: 3, End: 4}, + PosRange: posrange.PositionRange{Start: 3, End: 4}, }, }, }, @@ -302,11 +304,11 @@ var testExpr = []struct { Op: MUL, LHS: &NumberLiteral{ Val: -1, - PosRange: PositionRange{Start: 0, End: 2}, + PosRange: posrange.PositionRange{Start: 0, End: 2}, }, RHS: &NumberLiteral{ Val: 2, - PosRange: PositionRange{Start: 3, End: 4}, + PosRange: posrange.PositionRange{Start: 3, End: 4}, }, }, }, @@ -316,11 +318,11 @@ var testExpr = []struct { Op: ADD, LHS: &NumberLiteral{ Val: -1, - PosRange: PositionRange{Start: 0, End: 2}, + PosRange: posrange.PositionRange{Start: 0, End: 2}, }, RHS: &NumberLiteral{ Val: 2, - PosRange: PositionRange{Start: 3, End: 4}, + PosRange: posrange.PositionRange{Start: 3, End: 4}, }, }, }, @@ -332,11 +334,11 @@ var testExpr = []struct { Op: POW, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 1, End: 2}, + PosRange: posrange.PositionRange{Start: 1, End: 2}, }, RHS: &NumberLiteral{ Val: -2, - PosRange: PositionRange{Start: 3, End: 5}, + PosRange: posrange.PositionRange{Start: 3, End: 5}, }, }, }, @@ -347,17 +349,17 @@ var testExpr = []struct { Op: ADD, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 2}, + PosRange: posrange.PositionRange{Start: 0, End: 2}, }, RHS: &BinaryExpr{ Op: MUL, LHS: &NumberLiteral{ Val: -2, - PosRange: PositionRange{Start: 5, End: 7}, + PosRange: posrange.PositionRange{Start: 5, End: 7}, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 10, End: 11}, + PosRange: posrange.PositionRange{Start: 10, End: 11}, }, }, }, @@ -368,27 +370,27 @@ var testExpr = []struct { Op: ADD, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, RHS: &BinaryExpr{ Op: DIV, LHS: &NumberLiteral{ Val: 2, - PosRange: PositionRange{Start: 4, End: 5}, + PosRange: posrange.PositionRange{Start: 4, End: 5}, }, RHS: &ParenExpr{ Expr: &BinaryExpr{ Op: MUL, LHS: &NumberLiteral{ Val: 3, - PosRange: PositionRange{Start: 7, End: 8}, + PosRange: posrange.PositionRange{Start: 7, End: 8}, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 9, End: 10}, + PosRange: posrange.PositionRange{Start: 9, End: 10}, }, }, - PosRange: PositionRange{Start: 6, End: 11}, + PosRange: posrange.PositionRange{Start: 6, End: 11}, }, }, }, @@ -400,23 +402,23 @@ var testExpr = []struct { ReturnBool: true, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 0, End: 1}, + PosRange: posrange.PositionRange{Start: 0, End: 1}, }, RHS: &BinaryExpr{ Op: SUB, LHS: &NumberLiteral{ Val: 2, - PosRange: PositionRange{Start: 9, End: 10}, + PosRange: posrange.PositionRange{Start: 9, End: 10}, }, RHS: &BinaryExpr{ Op: MUL, LHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 13, End: 14}, + PosRange: posrange.PositionRange{Start: 13, End: 14}, }, RHS: &NumberLiteral{ Val: 2, - PosRange: PositionRange{Start: 17, End: 18}, + PosRange: posrange.PositionRange{Start: 17, End: 18}, }, }, }, @@ -431,7 +433,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 1, End: 12, }, @@ -447,7 +449,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 1, End: 12, }, @@ -463,7 +465,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 2, End: 13, }, @@ -606,7 +608,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -616,7 +618,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 6, End: 9, }, @@ -633,7 +635,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -643,7 +645,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "sum"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 6, End: 9, }, @@ -660,14 +662,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 7, End: 8}, + PosRange: posrange.PositionRange{Start: 7, End: 8}, }, }, }, @@ -680,14 +682,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, }, RHS: &NumberLiteral{ Val: 1, - PosRange: PositionRange{Start: 12, End: 13}, + PosRange: posrange.PositionRange{Start: 12, End: 13}, }, ReturnBool: true, }, @@ -698,14 +700,14 @@ var testExpr = []struct { Op: DIV, LHS: &NumberLiteral{ Val: 2.5, - PosRange: PositionRange{Start: 0, End: 3}, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RHS: &VectorSelector{ Name: "bar", LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 6, End: 9, }, @@ -721,7 +723,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -731,7 +733,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 8, End: 11, }, @@ -748,7 +750,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -758,7 +760,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 7, End: 10, }, @@ -775,7 +777,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -785,7 +787,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 11, End: 14, }, @@ -805,7 +807,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -815,7 +817,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 6, End: 9, }, @@ -829,7 +831,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bla"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 13, End: 16, }, @@ -839,7 +841,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "blub"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 21, End: 25, }, @@ -863,7 +865,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -873,7 +875,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 8, End: 11, }, @@ -885,7 +887,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "baz"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 19, End: 22, }, @@ -897,7 +899,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "qux"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 26, End: 29, }, @@ -915,7 +917,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -927,7 +929,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bla"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 14, End: 17, }, @@ -937,7 +939,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "blub"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 51, End: 55, }, @@ -965,7 +967,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -975,7 +977,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 20, End: 23, }, @@ -996,7 +998,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1006,7 +1008,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 31, End: 34, }, @@ -1027,7 +1029,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1037,7 +1039,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 22, End: 25, }, @@ -1058,7 +1060,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1068,7 +1070,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 13, End: 16, }, @@ -1089,7 +1091,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1099,7 +1101,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 28, End: 31, }, @@ -1119,7 +1121,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1129,7 +1131,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 19, End: 22, }, @@ -1149,7 +1151,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1159,7 +1161,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "baz"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 19, End: 22, }, @@ -1180,7 +1182,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1190,7 +1192,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 36, End: 39, }, @@ -1212,7 +1214,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1222,7 +1224,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 43, End: 46, }, @@ -1243,7 +1245,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1253,7 +1255,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 42, End: 45, }, @@ -1274,7 +1276,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1284,7 +1286,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 41, End: 44, }, @@ -1306,7 +1308,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1316,7 +1318,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 47, End: 50, }, @@ -1431,7 +1433,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1444,7 +1446,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "min"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1458,7 +1460,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 13, }, @@ -1472,7 +1474,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 14, }, @@ -1486,7 +1488,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 16, }, @@ -1500,7 +1502,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 17, }, @@ -1514,7 +1516,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 16, }, @@ -1528,7 +1530,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 10, }, @@ -1542,7 +1544,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 8, }, @@ -1556,7 +1558,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 8, }, @@ -1570,7 +1572,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 10, }, @@ -1584,7 +1586,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 12, }, @@ -1598,7 +1600,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 12, }, @@ -1612,7 +1614,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 9, }, @@ -1626,7 +1628,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 10, }, @@ -1640,7 +1642,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 9, }, @@ -1654,7 +1656,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 12, }, @@ -1693,7 +1695,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "a", "bc"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo:bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 15, }, @@ -1707,7 +1709,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "NaN", "bc"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 13, }, @@ -1721,7 +1723,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "}"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 12, }, @@ -1738,7 +1740,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchNotRegexp, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 48, }, @@ -1755,7 +1757,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchNotRegexp, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 49, }, @@ -1877,7 +1879,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 4, }, @@ -1894,7 +1896,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 4, }, @@ -1911,7 +1913,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -1929,7 +1931,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 4, }, @@ -1947,7 +1949,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 4, }, @@ -1965,7 +1967,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 4, }, @@ -1984,7 +1986,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "a", "b"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 11, }, @@ -2003,7 +2005,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "a", "b"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 11, }, @@ -2111,13 +2113,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 13, End: 24, }, }, Grouping: []string{"foo"}, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 25, }, @@ -2132,13 +2134,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 13, End: 24, }, }, Grouping: []string{"foo"}, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 25, }, @@ -2153,13 +2155,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 13, End: 24, }, }, Grouping: []string{"foo"}, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 25, }, @@ -2175,13 +2177,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 19, End: 30, }, }, Grouping: []string{"foo"}, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 31, }, @@ -2197,13 +2199,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 5, End: 16, }, }, Grouping: []string{"foo"}, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 31, }, @@ -2218,12 +2220,12 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 7, End: 18, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 19, }, @@ -2238,13 +2240,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 16, End: 27, }, }, Grouping: []string{"foo"}, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 28, }, @@ -2259,13 +2261,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 10, End: 21, }, }, Grouping: []string{}, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 22, }, @@ -2280,13 +2282,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 18, End: 29, }, }, Grouping: []string{"foo", "bar"}, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 30, }, @@ -2301,13 +2303,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 14, End: 25, }, }, Grouping: []string{"foo"}, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 26, }, @@ -2322,19 +2324,19 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 8, End: 19, }, }, Param: &NumberLiteral{ Val: 5, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 5, End: 6, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 20, }, @@ -2349,19 +2351,19 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 22, End: 33, }, }, Param: &StringLiteral{ Val: "value", - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 13, End: 20, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 34, }, @@ -2378,13 +2380,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 53, End: 64, }, }, Grouping: []string{"and", "by", "avg", "count", "alert", "annotations"}, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 65, }, @@ -2476,7 +2478,7 @@ var testExpr = []struct { expected: &Call{ Func: MustGetFunction("time"), Args: Expressions{}, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 6, }, @@ -2493,13 +2495,13 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchNotEqual, "foo", "bar"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 6, End: 29, }, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 30, }, @@ -2516,7 +2518,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 5, End: 16, }, @@ -2525,7 +2527,7 @@ var testExpr = []struct { EndPos: 20, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 21, }, @@ -2541,13 +2543,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 6, End: 17, }, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 18, }, @@ -2563,20 +2565,20 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 6, End: 17, }, }, &NumberLiteral{ Val: 5, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 19, End: 20, }, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 21, }, @@ -2668,12 +2670,12 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "sum"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 4, End: 7, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 8, }, @@ -2688,7 +2690,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "a"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 1, }, @@ -2698,7 +2700,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "sum"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 4, End: 7, }, @@ -2711,42 +2713,42 @@ var testExpr = []struct { input: `"double-quoted string \" with escaped quote"`, expected: &StringLiteral{ Val: "double-quoted string \" with escaped quote", - PosRange: PositionRange{Start: 0, End: 44}, + PosRange: posrange.PositionRange{Start: 0, End: 44}, }, }, { input: `'single-quoted string \' with escaped quote'`, expected: &StringLiteral{ Val: "single-quoted string ' with escaped quote", - PosRange: PositionRange{Start: 0, End: 44}, + PosRange: posrange.PositionRange{Start: 0, End: 44}, }, }, { input: "`backtick-quoted string`", expected: &StringLiteral{ Val: "backtick-quoted string", - PosRange: PositionRange{Start: 0, End: 24}, + PosRange: posrange.PositionRange{Start: 0, End: 24}, }, }, { input: `"\a\b\f\n\r\t\v\\\" - \xFF\377\u1234\U00010111\U0001011111☺"`, expected: &StringLiteral{ Val: "\a\b\f\n\r\t\v\\\" - \xFF\377\u1234\U00010111\U0001011111☺", - PosRange: PositionRange{Start: 0, End: 62}, + PosRange: posrange.PositionRange{Start: 0, End: 62}, }, }, { input: `'\a\b\f\n\r\t\v\\\' - \xFF\377\u1234\U00010111\U0001011111☺'`, expected: &StringLiteral{ Val: "\a\b\f\n\r\t\v\\' - \xFF\377\u1234\U00010111\U0001011111☺", - PosRange: PositionRange{Start: 0, End: 62}, + PosRange: posrange.PositionRange{Start: 0, End: 62}, }, }, { input: "`" + `\a\b\f\n\r\t\v\\\"\' - \xFF\377\u1234\U00010111\U0001011111☺` + "`", expected: &StringLiteral{ Val: `\a\b\f\n\r\t\v\\\"\' - \xFF\377\u1234\U00010111\U0001011111☺`, - PosRange: PositionRange{Start: 0, End: 64}, + PosRange: posrange.PositionRange{Start: 0, End: 64}, }, }, { @@ -2779,7 +2781,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 14, }, @@ -2798,7 +2800,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 14, }, @@ -2816,7 +2818,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -2841,7 +2843,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 19, End: 33, }, @@ -2850,7 +2852,7 @@ var testExpr = []struct { EndPos: 37, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 14, End: 38, }, @@ -2861,7 +2863,7 @@ var testExpr = []struct { EndPos: 45, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 46, }, @@ -2884,7 +2886,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 19, End: 33, }, @@ -2893,7 +2895,7 @@ var testExpr = []struct { EndPos: 37, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 14, End: 38, }, @@ -2902,7 +2904,7 @@ var testExpr = []struct { EndPos: 43, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 44, }, @@ -2929,7 +2931,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 19, End: 33, }, @@ -2938,7 +2940,7 @@ var testExpr = []struct { EndPos: 37, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 14, End: 38, }, @@ -2948,7 +2950,7 @@ var testExpr = []struct { EndPos: 53, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 54, }, @@ -2975,7 +2977,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 19, End: 33, }, @@ -2984,7 +2986,7 @@ var testExpr = []struct { EndPos: 37, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 14, End: 38, }, @@ -2994,7 +2996,7 @@ var testExpr = []struct { EndPos: 56, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 57, }, @@ -3021,7 +3023,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 19, End: 33, }, @@ -3030,7 +3032,7 @@ var testExpr = []struct { EndPos: 37, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 14, End: 38, }, @@ -3040,7 +3042,7 @@ var testExpr = []struct { EndPos: 56, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 57, }, @@ -3061,13 +3063,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 53, End: 64, }, }, Grouping: []string{"and", "by", "avg", "count", "alert", "annotations"}, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 65, }, @@ -3085,7 +3087,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 21, }, @@ -3104,7 +3106,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 17, }, @@ -3123,7 +3125,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 27, }, @@ -3143,7 +3145,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 27, }, @@ -3163,7 +3165,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 11, }, @@ -3189,7 +3191,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 1, End: 4, }, @@ -3200,13 +3202,13 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "nm", "val"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 7, End: 20, }, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 21, }, @@ -3229,7 +3231,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 1, End: 4, }, @@ -3240,13 +3242,13 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "nm", "val"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 7, End: 20, }, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 21, }, @@ -3270,7 +3272,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 1, End: 4, }, @@ -3282,13 +3284,13 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, Timestamp: makeInt64Pointer(1234000), - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 7, End: 27, }, }, }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 28, }, @@ -3327,7 +3329,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 13, }, @@ -3341,7 +3343,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 11, }, @@ -3356,7 +3358,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 4, }, @@ -3374,7 +3376,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 4, }, @@ -3391,7 +3393,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -3410,7 +3412,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -3439,7 +3441,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "start"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 5, }, @@ -3452,7 +3454,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "end"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -3466,7 +3468,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "end", "foo"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "start"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 16, }, @@ -3480,7 +3482,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "start", "foo"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "end"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 16, }, @@ -3495,7 +3497,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -3505,7 +3507,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 21, End: 24, }, @@ -3526,7 +3528,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 0, End: 3, }, @@ -3536,7 +3538,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: PositionRange{ + PosRange: posrange.PositionRange{ Start: 19, End: 22, }, @@ -3579,7 +3581,7 @@ func TestParseExpressions(t *testing.T) { for _, e := range errorList { require.True(t, 0 <= e.PositionRange.Start, "parse error has negative position\nExpression '%s'\nError: %v", test.input, e) require.True(t, e.PositionRange.Start <= e.PositionRange.End, "parse error has negative length\nExpression '%s'\nError: %v", test.input, e) - require.True(t, e.PositionRange.End <= Pos(len(test.input)), "parse error is not contained in input\nExpression '%s'\nError: %v", test.input, e) + require.True(t, e.PositionRange.End <= posrange.Pos(len(test.input)), "parse error is not contained in input\nExpression '%s'\nError: %v", test.input, e) } } }) diff --git a/promql/parser/posrange/posrange.go b/promql/parser/posrange/posrange.go new file mode 100644 index 000000000..531fd8a30 --- /dev/null +++ b/promql/parser/posrange/posrange.go @@ -0,0 +1,54 @@ +// Copyright 2023 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// posrange is used to report a position in query strings for error +// and warning messages. +package posrange + +import "fmt" + +// Pos is the position in a string. +// Negative numbers indicate undefined positions. +type Pos int + +// PositionRange describes a position in the input string of the parser. +type PositionRange struct { + Start Pos + End Pos +} + +// StartPosInput uses the query string to convert the PositionRange into a +// line:col string, indicating when this is not possible if the query is empty +// or the position is invalid. When this is used to convert ParseErr to a string, +// lineOffset is an additional line offset to be added, and is only used inside +// unit tests. +func (p PositionRange) StartPosInput(query string, lineOffset int) string { + if query == "" { + return "unknown position" + } + pos := int(p.Start) + if pos < 0 || pos > len(query) { + return "invalid position" + } + + lastLineBreak := -1 + line := lineOffset + 1 + for i, c := range query[:pos] { + if c == '\n' { + lastLineBreak = i + line++ + } + } + col := pos - lastLineBreak + return fmt.Sprintf("%d:%d", line, col) +} diff --git a/promql/test.go b/promql/test.go index f0fd7dab3..f6a31ee43 100644 --- a/promql/test.go +++ b/promql/test.go @@ -34,6 +34,7 @@ import ( "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/timestamp" "github.com/prometheus/prometheus/promql/parser" + "github.com/prometheus/prometheus/promql/parser/posrange" "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/util/teststorage" "github.com/prometheus/prometheus/util/testutil" @@ -197,7 +198,7 @@ func (t *test) parseEval(lines []string, i int) (int, *evalCmd, error) { if err != nil { parser.EnrichParseError(err, func(parseErr *parser.ParseErr) { parseErr.LineOffset = i - posOffset := parser.Pos(strings.Index(lines[i], expr)) + posOffset := posrange.Pos(strings.Index(lines[i], expr)) parseErr.PositionRange.Start += posOffset parseErr.PositionRange.End += posOffset parseErr.Query = lines[i] diff --git a/promql/value.go b/promql/value.go index 1b2a9d221..68e37f37e 100644 --- a/promql/value.go +++ b/promql/value.go @@ -24,8 +24,8 @@ import ( "github.com/prometheus/prometheus/model/histogram" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql/parser" - "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/tsdb/chunkenc" + "github.com/prometheus/prometheus/util/annotations" ) func (Matrix) Type() parser.ValueType { return parser.ValueTypeMatrix } @@ -303,7 +303,7 @@ func (m Matrix) ContainsSameLabelset() bool { type Result struct { Err error Value parser.Value - Warnings storage.Warnings + Warnings annotations.Annotations } // Vector returns a Vector if the result value is one. An error is returned if diff --git a/storage/fanout_test.go b/storage/fanout_test.go index e258eee03..0f9363d7a 100644 --- a/storage/fanout_test.go +++ b/storage/fanout_test.go @@ -24,6 +24,7 @@ import ( "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/tsdb/chunkenc" + "github.com/prometheus/prometheus/util/annotations" "github.com/prometheus/prometheus/util/teststorage" ) @@ -178,8 +179,9 @@ func TestFanoutErrors(t *testing.T) { if tc.warning != nil { require.Greater(t, len(ss.Warnings()), 0, "warnings expected") - require.Error(t, ss.Warnings()[0]) - require.Equal(t, tc.warning.Error(), ss.Warnings()[0].Error()) + w := ss.Warnings() + require.Error(t, w.AsErrors()[0]) + require.Equal(t, tc.warning.Error(), w.AsStrings("", 0)[0]) } }) t.Run("chunks", func(t *testing.T) { @@ -203,8 +205,9 @@ func TestFanoutErrors(t *testing.T) { if tc.warning != nil { require.Greater(t, len(ss.Warnings()), 0, "warnings expected") - require.Error(t, ss.Warnings()[0]) - require.Equal(t, tc.warning.Error(), ss.Warnings()[0].Error()) + w := ss.Warnings() + require.Error(t, w.AsErrors()[0]) + require.Equal(t, tc.warning.Error(), w.AsStrings("", 0)[0]) } }) } @@ -233,11 +236,11 @@ func (errQuerier) Select(context.Context, bool, *storage.SelectHints, ...*labels return storage.ErrSeriesSet(errSelect) } -func (errQuerier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, storage.Warnings, error) { +func (errQuerier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, annotations.Annotations, error) { return nil, nil, errors.New("label values error") } -func (errQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, storage.Warnings, error) { +func (errQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, annotations.Annotations, error) { return nil, nil, errors.New("label names error") } diff --git a/storage/generic.go b/storage/generic.go index a62ba4a89..e5f4b4d03 100644 --- a/storage/generic.go +++ b/storage/generic.go @@ -20,6 +20,7 @@ import ( "context" "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/util/annotations" ) type genericQuerier interface { @@ -31,7 +32,7 @@ type genericSeriesSet interface { Next() bool At() Labels Err() error - Warnings() Warnings + Warnings() annotations.Annotations } type genericSeriesMergeFunc func(...Labels) Labels @@ -139,4 +140,4 @@ func (noopGenericSeriesSet) At() Labels { return nil } func (noopGenericSeriesSet) Err() error { return nil } -func (noopGenericSeriesSet) Warnings() Warnings { return nil } +func (noopGenericSeriesSet) Warnings() annotations.Annotations { return nil } diff --git a/storage/interface.go b/storage/interface.go index 748e50ca7..79ca658eb 100644 --- a/storage/interface.go +++ b/storage/interface.go @@ -24,6 +24,7 @@ import ( "github.com/prometheus/prometheus/model/metadata" "github.com/prometheus/prometheus/tsdb/chunkenc" "github.com/prometheus/prometheus/tsdb/chunks" + "github.com/prometheus/prometheus/util/annotations" ) // The errors exposed. @@ -118,11 +119,11 @@ type MockQuerier struct { SelectMockFunction func(sortSeries bool, hints *SelectHints, matchers ...*labels.Matcher) SeriesSet } -func (q *MockQuerier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, Warnings, error) { +func (q *MockQuerier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, annotations.Annotations, error) { return nil, nil, nil } -func (q *MockQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, Warnings, error) { +func (q *MockQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, annotations.Annotations, error) { return nil, nil, nil } @@ -157,12 +158,12 @@ type LabelQuerier interface { // It is not safe to use the strings beyond the lifetime of the querier. // If matchers are specified the returned result set is reduced // to label values of metrics matching the matchers. - LabelValues(ctx context.Context, name string, matchers ...*labels.Matcher) ([]string, Warnings, error) + LabelValues(ctx context.Context, name string, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) // LabelNames returns all the unique label names present in the block in sorted order. // If matchers are specified the returned result set is reduced // to label names of metrics matching the matchers. - LabelNames(ctx context.Context, matchers ...*labels.Matcher) ([]string, Warnings, error) + LabelNames(ctx context.Context, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) // Close releases the resources of the Querier. Close() error @@ -307,7 +308,7 @@ type SeriesSet interface { Err() error // A collection of warnings for the whole set. // Warnings could be return even iteration has not failed with error. - Warnings() Warnings + Warnings() annotations.Annotations } var emptySeriesSet = errSeriesSet{} @@ -321,10 +322,10 @@ type testSeriesSet struct { series Series } -func (s testSeriesSet) Next() bool { return true } -func (s testSeriesSet) At() Series { return s.series } -func (s testSeriesSet) Err() error { return nil } -func (s testSeriesSet) Warnings() Warnings { return nil } +func (s testSeriesSet) Next() bool { return true } +func (s testSeriesSet) At() Series { return s.series } +func (s testSeriesSet) Err() error { return nil } +func (s testSeriesSet) Warnings() annotations.Annotations { return nil } // TestSeriesSet returns a mock series set func TestSeriesSet(series Series) SeriesSet { @@ -335,10 +336,10 @@ type errSeriesSet struct { err error } -func (s errSeriesSet) Next() bool { return false } -func (s errSeriesSet) At() Series { return nil } -func (s errSeriesSet) Err() error { return s.err } -func (s errSeriesSet) Warnings() Warnings { return nil } +func (s errSeriesSet) Next() bool { return false } +func (s errSeriesSet) At() Series { return nil } +func (s errSeriesSet) Err() error { return s.err } +func (s errSeriesSet) Warnings() annotations.Annotations { return nil } // ErrSeriesSet returns a series set that wraps an error. func ErrSeriesSet(err error) SeriesSet { @@ -356,10 +357,10 @@ type errChunkSeriesSet struct { err error } -func (s errChunkSeriesSet) Next() bool { return false } -func (s errChunkSeriesSet) At() ChunkSeries { return nil } -func (s errChunkSeriesSet) Err() error { return s.err } -func (s errChunkSeriesSet) Warnings() Warnings { return nil } +func (s errChunkSeriesSet) Next() bool { return false } +func (s errChunkSeriesSet) At() ChunkSeries { return nil } +func (s errChunkSeriesSet) Err() error { return s.err } +func (s errChunkSeriesSet) Warnings() annotations.Annotations { return nil } // ErrChunkSeriesSet returns a chunk series set that wraps an error. func ErrChunkSeriesSet(err error) ChunkSeriesSet { @@ -405,7 +406,7 @@ type ChunkSeriesSet interface { Err() error // A collection of warnings for the whole set. // Warnings could be return even iteration has not failed with error. - Warnings() Warnings + Warnings() annotations.Annotations } // ChunkSeries exposes a single time series and allows iterating over chunks. @@ -433,5 +434,3 @@ type ChunkIterable interface { // chunks of the series, sorted by min time. Iterator(chunks.Iterator) chunks.Iterator } - -type Warnings []error diff --git a/storage/lazy.go b/storage/lazy.go index 62f76cb6a..fab974c28 100644 --- a/storage/lazy.go +++ b/storage/lazy.go @@ -13,6 +13,10 @@ package storage +import ( + "github.com/prometheus/prometheus/util/annotations" +) + // lazyGenericSeriesSet is a wrapped series set that is initialised on first call to Next(). type lazyGenericSeriesSet struct { init func() (genericSeriesSet, bool) @@ -43,25 +47,25 @@ func (c *lazyGenericSeriesSet) At() Labels { return nil } -func (c *lazyGenericSeriesSet) Warnings() Warnings { +func (c *lazyGenericSeriesSet) Warnings() annotations.Annotations { if c.set != nil { return c.set.Warnings() } return nil } -type warningsOnlySeriesSet Warnings +type warningsOnlySeriesSet annotations.Annotations -func (warningsOnlySeriesSet) Next() bool { return false } -func (warningsOnlySeriesSet) Err() error { return nil } -func (warningsOnlySeriesSet) At() Labels { return nil } -func (c warningsOnlySeriesSet) Warnings() Warnings { return Warnings(c) } +func (warningsOnlySeriesSet) Next() bool { return false } +func (warningsOnlySeriesSet) Err() error { return nil } +func (warningsOnlySeriesSet) At() Labels { return nil } +func (c warningsOnlySeriesSet) Warnings() annotations.Annotations { return annotations.Annotations(c) } type errorOnlySeriesSet struct { err error } -func (errorOnlySeriesSet) Next() bool { return false } -func (errorOnlySeriesSet) At() Labels { return nil } -func (s errorOnlySeriesSet) Err() error { return s.err } -func (errorOnlySeriesSet) Warnings() Warnings { return nil } +func (errorOnlySeriesSet) Next() bool { return false } +func (errorOnlySeriesSet) At() Labels { return nil } +func (s errorOnlySeriesSet) Err() error { return s.err } +func (errorOnlySeriesSet) Warnings() annotations.Annotations { return nil } diff --git a/storage/merge.go b/storage/merge.go index 8c442b4f0..b4d254a23 100644 --- a/storage/merge.go +++ b/storage/merge.go @@ -28,6 +28,7 @@ import ( "github.com/prometheus/prometheus/tsdb/chunkenc" "github.com/prometheus/prometheus/tsdb/chunks" tsdb_errors "github.com/prometheus/prometheus/tsdb/errors" + "github.com/prometheus/prometheus/util/annotations" ) type mergeGenericQuerier struct { @@ -158,7 +159,7 @@ func (l labelGenericQueriers) SplitByHalf() (labelGenericQueriers, labelGenericQ // LabelValues returns all potential values for a label name. // If matchers are specified the returned result set is reduced // to label values of metrics matching the matchers. -func (q *mergeGenericQuerier) LabelValues(ctx context.Context, name string, matchers ...*labels.Matcher) ([]string, Warnings, error) { +func (q *mergeGenericQuerier) LabelValues(ctx context.Context, name string, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) { res, ws, err := q.lvals(ctx, q.queriers, name, matchers...) if err != nil { return nil, nil, fmt.Errorf("LabelValues() from merge generic querier for label %s: %w", name, err) @@ -167,7 +168,7 @@ func (q *mergeGenericQuerier) LabelValues(ctx context.Context, name string, matc } // lvals performs merge sort for LabelValues from multiple queriers. -func (q *mergeGenericQuerier) lvals(ctx context.Context, lq labelGenericQueriers, n string, matchers ...*labels.Matcher) ([]string, Warnings, error) { +func (q *mergeGenericQuerier) lvals(ctx context.Context, lq labelGenericQueriers, n string, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) { if lq.Len() == 0 { return nil, nil, nil } @@ -176,14 +177,14 @@ func (q *mergeGenericQuerier) lvals(ctx context.Context, lq labelGenericQueriers } a, b := lq.SplitByHalf() - var ws Warnings + var ws annotations.Annotations s1, w, err := q.lvals(ctx, a, n, matchers...) - ws = append(ws, w...) + ws.Merge(w) if err != nil { return nil, ws, err } s2, ws, err := q.lvals(ctx, b, n, matchers...) - ws = append(ws, w...) + ws.Merge(w) if err != nil { return nil, ws, err } @@ -218,16 +219,16 @@ func mergeStrings(a, b []string) []string { } // LabelNames returns all the unique label names present in all queriers in sorted order. -func (q *mergeGenericQuerier) LabelNames(ctx context.Context, matchers ...*labels.Matcher) ([]string, Warnings, error) { +func (q *mergeGenericQuerier) LabelNames(ctx context.Context, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) { var ( labelNamesMap = make(map[string]struct{}) - warnings Warnings + warnings annotations.Annotations ) for _, querier := range q.queriers { names, wrn, err := querier.LabelNames(ctx, matchers...) if wrn != nil { // TODO(bwplotka): We could potentially wrap warnings. - warnings = append(warnings, wrn...) + warnings.Merge(wrn) } if err != nil { return nil, nil, fmt.Errorf("LabelNames() from merge generic querier: %w", err) @@ -382,10 +383,10 @@ func (c *genericMergeSeriesSet) Err() error { return nil } -func (c *genericMergeSeriesSet) Warnings() Warnings { - var ws Warnings +func (c *genericMergeSeriesSet) Warnings() annotations.Annotations { + var ws annotations.Annotations for _, set := range c.sets { - ws = append(ws, set.Warnings()...) + ws.Merge(set.Warnings()) } return ws } diff --git a/storage/merge_test.go b/storage/merge_test.go index 01257ff67..0799907c9 100644 --- a/storage/merge_test.go +++ b/storage/merge_test.go @@ -28,6 +28,7 @@ import ( "github.com/prometheus/prometheus/tsdb/chunkenc" "github.com/prometheus/prometheus/tsdb/chunks" "github.com/prometheus/prometheus/tsdb/tsdbutil" + "github.com/prometheus/prometheus/util/annotations" ) func TestMergeQuerierWithChainMerger(t *testing.T) { @@ -777,7 +778,7 @@ func (m *mockSeriesSet) At() Series { return m.series[m.idx] } func (m *mockSeriesSet) Err() error { return nil } -func (m *mockSeriesSet) Warnings() Warnings { return nil } +func (m *mockSeriesSet) Warnings() annotations.Annotations { return nil } type mockChunkSeriesSet struct { idx int @@ -800,7 +801,7 @@ func (m *mockChunkSeriesSet) At() ChunkSeries { return m.series[m.idx] } func (m *mockChunkSeriesSet) Err() error { return nil } -func (m *mockChunkSeriesSet) Warnings() Warnings { return nil } +func (m *mockChunkSeriesSet) Warnings() annotations.Annotations { return nil } func TestChainSampleIterator(t *testing.T) { for _, tc := range []struct { @@ -974,7 +975,7 @@ type mockGenericQuerier struct { sortedSeriesRequested []bool resp []string - warnings Warnings + warnings annotations.Annotations err error } @@ -990,7 +991,7 @@ func (m *mockGenericQuerier) Select(_ context.Context, b bool, _ *SelectHints, _ return &mockGenericSeriesSet{resp: m.resp, warnings: m.warnings, err: m.err} } -func (m *mockGenericQuerier) LabelValues(_ context.Context, name string, matchers ...*labels.Matcher) ([]string, Warnings, error) { +func (m *mockGenericQuerier) LabelValues(_ context.Context, name string, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) { m.mtx.Lock() m.labelNamesRequested = append(m.labelNamesRequested, labelNameRequest{ name: name, @@ -1000,7 +1001,7 @@ func (m *mockGenericQuerier) LabelValues(_ context.Context, name string, matcher return m.resp, m.warnings, m.err } -func (m *mockGenericQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, Warnings, error) { +func (m *mockGenericQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, annotations.Annotations, error) { m.mtx.Lock() m.labelNamesCalls++ m.mtx.Unlock() @@ -1014,7 +1015,7 @@ func (m *mockGenericQuerier) Close() error { type mockGenericSeriesSet struct { resp []string - warnings Warnings + warnings annotations.Annotations err error curr int @@ -1031,8 +1032,8 @@ func (m *mockGenericSeriesSet) Next() bool { return true } -func (m *mockGenericSeriesSet) Err() error { return m.err } -func (m *mockGenericSeriesSet) Warnings() Warnings { return m.warnings } +func (m *mockGenericSeriesSet) Err() error { return m.err } +func (m *mockGenericSeriesSet) Warnings() annotations.Annotations { return m.warnings } func (m *mockGenericSeriesSet) At() Labels { return mockLabels(m.resp[m.curr-1]) @@ -1068,10 +1069,9 @@ func TestMergeGenericQuerierWithSecondaries_ErrorHandling(t *testing.T) { expectedSelectsSeries []labels.Labels expectedLabels []string - expectedWarnings [4]Warnings + expectedWarnings annotations.Annotations expectedErrs [4]error }{ - {}, { name: "one successful primary querier", queriers: []genericQuerier{&mockGenericQuerier{resp: []string{"a", "b"}, warnings: nil, err: nil}}, @@ -1145,31 +1145,21 @@ func TestMergeGenericQuerierWithSecondaries_ErrorHandling(t *testing.T) { expectedSelectsSeries: []labels.Labels{ labels.FromStrings("test", "a"), }, - expectedLabels: []string{"a"}, - expectedWarnings: [4]Warnings{ - []error{errStorage, errStorage}, - []error{errStorage, errStorage}, - []error{errStorage, errStorage}, - []error{errStorage, errStorage}, - }, + expectedLabels: []string{"a"}, + expectedWarnings: annotations.New().Add(errStorage), }, { name: "successful queriers with warnings", queriers: []genericQuerier{ - &mockGenericQuerier{resp: []string{"a"}, warnings: []error{warnStorage}, err: nil}, - &secondaryQuerier{genericQuerier: &mockGenericQuerier{resp: []string{"b"}, warnings: []error{warnStorage}, err: nil}}, + &mockGenericQuerier{resp: []string{"a"}, warnings: annotations.New().Add(warnStorage), err: nil}, + &secondaryQuerier{genericQuerier: &mockGenericQuerier{resp: []string{"b"}, warnings: annotations.New().Add(warnStorage), err: nil}}, }, expectedSelectsSeries: []labels.Labels{ labels.FromStrings("test", "a"), labels.FromStrings("test", "b"), }, - expectedLabels: []string{"a", "b"}, - expectedWarnings: [4]Warnings{ - []error{warnStorage, warnStorage}, - []error{warnStorage, warnStorage}, - []error{warnStorage, warnStorage}, - []error{warnStorage, warnStorage}, - }, + expectedLabels: []string{"a", "b"}, + expectedWarnings: annotations.New().Add(warnStorage), }, } { t.Run(tcase.name, func(t *testing.T) { @@ -1184,7 +1174,7 @@ func TestMergeGenericQuerierWithSecondaries_ErrorHandling(t *testing.T) { for res.Next() { lbls = append(lbls, res.At().Labels()) } - require.Equal(t, tcase.expectedWarnings[0], res.Warnings()) + require.Subset(t, tcase.expectedWarnings, res.Warnings()) require.Equal(t, tcase.expectedErrs[0], res.Err()) require.True(t, errors.Is(res.Err(), tcase.expectedErrs[0]), "expected error doesn't match") require.Equal(t, tcase.expectedSelectsSeries, lbls) @@ -1201,7 +1191,7 @@ func TestMergeGenericQuerierWithSecondaries_ErrorHandling(t *testing.T) { }) t.Run("LabelNames", func(t *testing.T) { res, w, err := q.LabelNames(ctx) - require.Equal(t, tcase.expectedWarnings[1], w) + require.Subset(t, tcase.expectedWarnings, w) require.True(t, errors.Is(err, tcase.expectedErrs[1]), "expected error doesn't match") require.Equal(t, tcase.expectedLabels, res) @@ -1216,7 +1206,7 @@ func TestMergeGenericQuerierWithSecondaries_ErrorHandling(t *testing.T) { }) t.Run("LabelValues", func(t *testing.T) { res, w, err := q.LabelValues(ctx, "test") - require.Equal(t, tcase.expectedWarnings[2], w) + require.Subset(t, tcase.expectedWarnings, w) require.True(t, errors.Is(err, tcase.expectedErrs[2]), "expected error doesn't match") require.Equal(t, tcase.expectedLabels, res) @@ -1232,7 +1222,7 @@ func TestMergeGenericQuerierWithSecondaries_ErrorHandling(t *testing.T) { t.Run("LabelValuesWithMatchers", func(t *testing.T) { matcher := labels.MustNewMatcher(labels.MatchEqual, "otherLabel", "someValue") res, w, err := q.LabelValues(ctx, "test2", matcher) - require.Equal(t, tcase.expectedWarnings[3], w) + require.Subset(t, tcase.expectedWarnings, w) require.True(t, errors.Is(err, tcase.expectedErrs[3]), "expected error doesn't match") require.Equal(t, tcase.expectedLabels, res) diff --git a/storage/noop.go b/storage/noop.go index d3bf0ef66..be5741ddd 100644 --- a/storage/noop.go +++ b/storage/noop.go @@ -17,6 +17,7 @@ import ( "context" "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/util/annotations" ) type noopQuerier struct{} @@ -30,11 +31,11 @@ func (noopQuerier) Select(context.Context, bool, *SelectHints, ...*labels.Matche return NoopSeriesSet() } -func (noopQuerier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, Warnings, error) { +func (noopQuerier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, annotations.Annotations, error) { return nil, nil, nil } -func (noopQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, Warnings, error) { +func (noopQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, annotations.Annotations, error) { return nil, nil, nil } @@ -53,11 +54,11 @@ func (noopChunkQuerier) Select(context.Context, bool, *SelectHints, ...*labels.M return NoopChunkedSeriesSet() } -func (noopChunkQuerier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, Warnings, error) { +func (noopChunkQuerier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, annotations.Annotations, error) { return nil, nil, nil } -func (noopChunkQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, Warnings, error) { +func (noopChunkQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, annotations.Annotations, error) { return nil, nil, nil } @@ -78,7 +79,7 @@ func (noopSeriesSet) At() Series { return nil } func (noopSeriesSet) Err() error { return nil } -func (noopSeriesSet) Warnings() Warnings { return nil } +func (noopSeriesSet) Warnings() annotations.Annotations { return nil } type noopChunkedSeriesSet struct{} @@ -93,4 +94,4 @@ func (noopChunkedSeriesSet) At() ChunkSeries { return nil } func (noopChunkedSeriesSet) Err() error { return nil } -func (noopChunkedSeriesSet) Warnings() Warnings { return nil } +func (noopChunkedSeriesSet) Warnings() annotations.Annotations { return nil } diff --git a/storage/remote/codec.go b/storage/remote/codec.go index 4927c16fd..4e0166d17 100644 --- a/storage/remote/codec.go +++ b/storage/remote/codec.go @@ -38,6 +38,7 @@ import ( "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/tsdb/chunkenc" "github.com/prometheus/prometheus/tsdb/chunks" + "github.com/prometheus/prometheus/util/annotations" ) const ( @@ -122,7 +123,7 @@ func ToQuery(from, to int64, matchers []*labels.Matcher, hints *storage.SelectHi } // ToQueryResult builds a QueryResult proto. -func ToQueryResult(ss storage.SeriesSet, sampleLimit int) (*prompb.QueryResult, storage.Warnings, error) { +func ToQueryResult(ss storage.SeriesSet, sampleLimit int) (*prompb.QueryResult, annotations.Annotations, error) { numSamples := 0 resp := &prompb.QueryResult{} var iter chunkenc.Iterator @@ -224,7 +225,7 @@ func StreamChunkedReadResponses( sortedExternalLabels []prompb.Label, maxBytesInFrame int, marshalPool *sync.Pool, -) (storage.Warnings, error) { +) (annotations.Annotations, error) { var ( chks []prompb.Chunk lbls []prompb.Label @@ -340,7 +341,7 @@ func (e errSeriesSet) Err() error { return e.err } -func (e errSeriesSet) Warnings() storage.Warnings { return nil } +func (e errSeriesSet) Warnings() annotations.Annotations { return nil } // concreteSeriesSet implements storage.SeriesSet. type concreteSeriesSet struct { @@ -361,7 +362,7 @@ func (c *concreteSeriesSet) Err() error { return nil } -func (c *concreteSeriesSet) Warnings() storage.Warnings { return nil } +func (c *concreteSeriesSet) Warnings() annotations.Annotations { return nil } // concreteSeries implements storage.Series. type concreteSeries struct { diff --git a/storage/remote/codec_test.go b/storage/remote/codec_test.go index dbd5cec21..585bdfd88 100644 --- a/storage/remote/codec_test.go +++ b/storage/remote/codec_test.go @@ -30,6 +30,7 @@ import ( "github.com/prometheus/prometheus/tsdb/chunkenc" "github.com/prometheus/prometheus/tsdb/chunks" "github.com/prometheus/prometheus/tsdb/tsdbutil" + "github.com/prometheus/prometheus/util/annotations" ) var testHistogram = histogram.Histogram{ @@ -810,7 +811,7 @@ func (c *mockChunkSeriesSet) At() storage.ChunkSeries { } } -func (c *mockChunkSeriesSet) Warnings() storage.Warnings { return nil } +func (c *mockChunkSeriesSet) Warnings() annotations.Annotations { return nil } func (c *mockChunkSeriesSet) Err() error { return nil diff --git a/storage/remote/read.go b/storage/remote/read.go index b4cd9a5f4..723030091 100644 --- a/storage/remote/read.go +++ b/storage/remote/read.go @@ -20,6 +20,7 @@ import ( "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/storage" + "github.com/prometheus/prometheus/util/annotations" ) type sampleAndChunkQueryableClient struct { @@ -209,13 +210,13 @@ func (q querier) addExternalLabels(ms []*labels.Matcher) ([]*labels.Matcher, []s } // LabelValues implements storage.Querier and is a noop. -func (q *querier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, storage.Warnings, error) { +func (q *querier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, annotations.Annotations, error) { // TODO: Implement: https://github.com/prometheus/prometheus/issues/3351 return nil, nil, errors.New("not implemented") } // LabelNames implements storage.Querier and is a noop. -func (q *querier) LabelNames(context.Context, ...*labels.Matcher) ([]string, storage.Warnings, error) { +func (q *querier) LabelNames(context.Context, ...*labels.Matcher) ([]string, annotations.Annotations, error) { // TODO: Implement: https://github.com/prometheus/prometheus/issues/3351 return nil, nil, errors.New("not implemented") } diff --git a/storage/remote/read_handler.go b/storage/remote/read_handler.go index baa19e3ac..5cb4d3977 100644 --- a/storage/remote/read_handler.go +++ b/storage/remote/read_handler.go @@ -27,6 +27,7 @@ import ( "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/prompb" "github.com/prometheus/prometheus/storage" + "github.com/prometheus/prometheus/util/annotations" "github.com/prometheus/prometheus/util/gate" ) @@ -154,7 +155,7 @@ func (h *readHandler) remoteReadSamples( } } - var ws storage.Warnings + var ws annotations.Annotations resp.Results[i], ws, err = ToQueryResult(querier.Select(ctx, false, hints, filteredMatchers...), h.remoteReadSampleLimit) if err != nil { return err diff --git a/storage/remote/read_test.go b/storage/remote/read_test.go index be4bac24f..54d4825f6 100644 --- a/storage/remote/read_test.go +++ b/storage/remote/read_test.go @@ -27,7 +27,7 @@ import ( "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/prompb" - "github.com/prometheus/prometheus/storage" + "github.com/prometheus/prometheus/util/annotations" ) func TestNoDuplicateReadConfigs(t *testing.T) { @@ -475,7 +475,7 @@ func TestSampleAndChunkQueryableClient(t *testing.T) { ss := q.Select(context.Background(), true, nil, tc.matchers...) require.NoError(t, err) - require.Equal(t, storage.Warnings(nil), ss.Warnings()) + require.Equal(t, annotations.Annotations(nil), ss.Warnings()) require.Equal(t, tc.expectedQuery, m.got) diff --git a/storage/secondary.go b/storage/secondary.go index 8dd30f9f0..44d978183 100644 --- a/storage/secondary.go +++ b/storage/secondary.go @@ -18,6 +18,7 @@ import ( "sync" "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/util/annotations" ) // secondaryQuerier is a wrapper that allows a querier to be treated in a best effort manner. @@ -48,18 +49,18 @@ func newSecondaryQuerierFromChunk(cq ChunkQuerier) genericQuerier { return &secondaryQuerier{genericQuerier: newGenericQuerierFromChunk(cq)} } -func (s *secondaryQuerier) LabelValues(ctx context.Context, name string, matchers ...*labels.Matcher) ([]string, Warnings, error) { +func (s *secondaryQuerier) LabelValues(ctx context.Context, name string, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) { vals, w, err := s.genericQuerier.LabelValues(ctx, name, matchers...) if err != nil { - return nil, append([]error{err}, w...), nil + return nil, w.Add(err), nil } return vals, w, nil } -func (s *secondaryQuerier) LabelNames(ctx context.Context, matchers ...*labels.Matcher) ([]string, Warnings, error) { +func (s *secondaryQuerier) LabelNames(ctx context.Context, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) { names, w, err := s.genericQuerier.LabelNames(ctx, matchers...) if err != nil { - return nil, append([]error{err}, w...), nil + return nil, w.Add(err), nil } return names, w, nil } @@ -83,7 +84,7 @@ func (s *secondaryQuerier) Select(ctx context.Context, sortSeries bool, hints *S if err := set.Err(); err != nil { // One of the sets failed, ensure current one returning errors as warnings, and rest of the sets return nothing. // (All or nothing logic). - s.asyncSets[curr] = warningsOnlySeriesSet(append([]error{err}, ws...)) + s.asyncSets[curr] = warningsOnlySeriesSet(ws.Add(err)) for i := range s.asyncSets { if curr == i { continue diff --git a/tsdb/db_test.go b/tsdb/db_test.go index ce7e0fb3b..6fda4e3bd 100644 --- a/tsdb/db_test.go +++ b/tsdb/db_test.go @@ -53,6 +53,7 @@ import ( "github.com/prometheus/prometheus/tsdb/tombstones" "github.com/prometheus/prometheus/tsdb/tsdbutil" "github.com/prometheus/prometheus/tsdb/wlog" + "github.com/prometheus/prometheus/util/annotations" "github.com/prometheus/prometheus/util/testutil" ) @@ -1731,7 +1732,7 @@ func TestNotMatcherSelectsLabelsUnsetSeries(t *testing.T) { // expandSeriesSet returns the raw labels in the order they are retrieved from // the series set and the samples keyed by Labels().String(). -func expandSeriesSet(ss storage.SeriesSet) ([]labels.Labels, map[string][]sample, storage.Warnings, error) { +func expandSeriesSet(ss storage.SeriesSet) ([]labels.Labels, map[string][]sample, annotations.Annotations, error) { resultLabels := []labels.Labels{} resultSamples := map[string][]sample{} var it chunkenc.Iterator @@ -1932,7 +1933,7 @@ func TestQuerierWithBoundaryChunks(t *testing.T) { // The requested interval covers 2 blocks, so the querier's label values for blockID should give us 2 values, one from each block. b, ws, err := q.LabelValues(ctx, "blockID") require.NoError(t, err) - require.Equal(t, storage.Warnings(nil), ws) + require.Equal(t, annotations.Annotations{}, ws) require.Equal(t, []string{"1", "2"}, b) } @@ -2235,7 +2236,7 @@ func TestDB_LabelNames(t *testing.T) { // Testing DB (union). q, err := db.Querier(math.MinInt64, math.MaxInt64) require.NoError(t, err) - var ws storage.Warnings + var ws annotations.Annotations labelNames, ws, err = q.LabelNames(ctx) require.NoError(t, err) require.Equal(t, 0, len(ws)) diff --git a/tsdb/querier.go b/tsdb/querier.go index 81a969f38..f791fd640 100644 --- a/tsdb/querier.go +++ b/tsdb/querier.go @@ -32,6 +32,7 @@ import ( tsdb_errors "github.com/prometheus/prometheus/tsdb/errors" "github.com/prometheus/prometheus/tsdb/index" "github.com/prometheus/prometheus/tsdb/tombstones" + "github.com/prometheus/prometheus/util/annotations" ) // Bitmap used by func isRegexMetaCharacter to check whether a character needs to be escaped. @@ -89,12 +90,12 @@ func newBlockBaseQuerier(b BlockReader, mint, maxt int64) (*blockBaseQuerier, er }, nil } -func (q *blockBaseQuerier) LabelValues(ctx context.Context, name string, matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { +func (q *blockBaseQuerier) LabelValues(ctx context.Context, name string, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) { res, err := q.index.SortedLabelValues(ctx, name, matchers...) return res, nil, err } -func (q *blockBaseQuerier) LabelNames(ctx context.Context, matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { +func (q *blockBaseQuerier) LabelNames(ctx context.Context, matchers ...*labels.Matcher) ([]string, annotations.Annotations, error) { res, err := q.index.LabelNames(ctx, matchers...) return res, nil, err } @@ -615,7 +616,7 @@ func (b *blockBaseSeriesSet) Err() error { return b.p.Err() } -func (b *blockBaseSeriesSet) Warnings() storage.Warnings { return nil } +func (b *blockBaseSeriesSet) Warnings() annotations.Annotations { return nil } // populateWithDelGenericSeriesIterator allows to iterate over given chunk // metas. In each iteration it ensures that chunks are trimmed based on given diff --git a/tsdb/querier_test.go b/tsdb/querier_test.go index b18061304..2fcdbf7c4 100644 --- a/tsdb/querier_test.go +++ b/tsdb/querier_test.go @@ -38,20 +38,21 @@ import ( "github.com/prometheus/prometheus/tsdb/index" "github.com/prometheus/prometheus/tsdb/tombstones" "github.com/prometheus/prometheus/tsdb/tsdbutil" + "github.com/prometheus/prometheus/util/annotations" ) // TODO(bwplotka): Replace those mocks with remote.concreteSeriesSet. type mockSeriesSet struct { next func() bool series func() storage.Series - ws func() storage.Warnings + ws func() annotations.Annotations err func() error } -func (m *mockSeriesSet) Next() bool { return m.next() } -func (m *mockSeriesSet) At() storage.Series { return m.series() } -func (m *mockSeriesSet) Err() error { return m.err() } -func (m *mockSeriesSet) Warnings() storage.Warnings { return m.ws() } +func (m *mockSeriesSet) Next() bool { return m.next() } +func (m *mockSeriesSet) At() storage.Series { return m.series() } +func (m *mockSeriesSet) Err() error { return m.err() } +func (m *mockSeriesSet) Warnings() annotations.Annotations { return m.ws() } func newMockSeriesSet(list []storage.Series) *mockSeriesSet { i := -1 @@ -64,21 +65,21 @@ func newMockSeriesSet(list []storage.Series) *mockSeriesSet { return list[i] }, err: func() error { return nil }, - ws: func() storage.Warnings { return nil }, + ws: func() annotations.Annotations { return nil }, } } type mockChunkSeriesSet struct { next func() bool series func() storage.ChunkSeries - ws func() storage.Warnings + ws func() annotations.Annotations err func() error } -func (m *mockChunkSeriesSet) Next() bool { return m.next() } -func (m *mockChunkSeriesSet) At() storage.ChunkSeries { return m.series() } -func (m *mockChunkSeriesSet) Err() error { return m.err() } -func (m *mockChunkSeriesSet) Warnings() storage.Warnings { return m.ws() } +func (m *mockChunkSeriesSet) Next() bool { return m.next() } +func (m *mockChunkSeriesSet) At() storage.ChunkSeries { return m.series() } +func (m *mockChunkSeriesSet) Err() error { return m.err() } +func (m *mockChunkSeriesSet) Warnings() annotations.Annotations { return m.ws() } func newMockChunkSeriesSet(list []storage.ChunkSeries) *mockChunkSeriesSet { i := -1 @@ -91,7 +92,7 @@ func newMockChunkSeriesSet(list []storage.ChunkSeries) *mockChunkSeriesSet { return list[i] }, err: func() error { return nil }, - ws: func() storage.Warnings { return nil }, + ws: func() annotations.Annotations { return nil }, } } diff --git a/util/annotations/annotations.go b/util/annotations/annotations.go new file mode 100644 index 000000000..e8b59dc7f --- /dev/null +++ b/util/annotations/annotations.go @@ -0,0 +1,165 @@ +// Copyright 2023 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package annotations + +import ( + "errors" + "fmt" + + "github.com/prometheus/common/model" + + "github.com/prometheus/prometheus/promql/parser/posrange" +) + +// Annotations is a general wrapper for warnings and other information +// that is returned by the query API along with the results. +// Each individual annotation is modeled by a Go error. +// They are deduplicated based on the string returned by error.Error(). +// The zero value is usable without further initialization, see New(). +type Annotations map[string]error + +// New returns new Annotations ready to use. Note that the zero value of +// Annotations is also fully usable, but using this method is often more +// readable. +func New() *Annotations { + return &Annotations{} +} + +// Add adds an annotation (modeled as a Go error) in-place and returns the +// modified Annotations for convenience. +func (a *Annotations) Add(err error) Annotations { + if *a == nil { + *a = Annotations{} + } + (*a)[err.Error()] = err + return *a +} + +// Merge adds the contents of the second annotation to the first, modifying +// the first in-place, and returns the merged first Annotation for convenience. +func (a *Annotations) Merge(aa Annotations) Annotations { + if *a == nil { + *a = Annotations{} + } + for key, val := range aa { + (*a)[key] = val + } + return *a +} + +// AsErrors is a convenience function to return the annotations map as a slice +// of errors. +func (a Annotations) AsErrors() []error { + arr := make([]error, 0, len(a)) + for _, err := range a { + arr = append(arr, err) + } + return arr +} + +// AsStrings is a convenience function to return the annotations map as a slice +// of strings. The query string is used to get the line number and character offset +// positioning info of the elements which trigger an annotation. We limit the number +// of annotations returned here with maxAnnos (0 for no limit). +func (a Annotations) AsStrings(query string, maxAnnos int) []string { + arr := make([]string, 0, len(a)) + for _, err := range a { + if maxAnnos > 0 && len(arr) >= maxAnnos { + break + } + anErr, ok := err.(annoErr) + if ok { + anErr.Query = query + err = anErr + } + arr = append(arr, err.Error()) + } + if maxAnnos > 0 && len(a) > maxAnnos { + arr = append(arr, fmt.Sprintf("%d more annotations omitted", len(a)-maxAnnos)) + } + return arr +} + +//nolint:revive // Ignore ST1012 +var ( + // Currently there are only 2 types, warnings and info. + // For now, info are visually identical with warnings as we have not updated + // the API spec or the frontend to show a different kind of warning. But we + // make the distinction here to prepare for adding them in future. + PromQLInfo = errors.New("PromQL info") + PromQLWarning = errors.New("PromQL warning") + + InvalidQuantileWarning = fmt.Errorf("%w: quantile value should be between 0 and 1", PromQLWarning) + BadBucketLabelWarning = fmt.Errorf("%w: bucket label %q is missing or has a malformed value", PromQLWarning, model.BucketLabel) + MixedFloatsHistogramsWarning = fmt.Errorf("%w: encountered a mix of histograms and floats for metric name", PromQLWarning) + MixedClassicNativeHistogramsWarning = fmt.Errorf("%w: vector contains a mix of classic and native histograms for metric name", PromQLWarning) + + PossibleNonCounterInfo = fmt.Errorf("%w: metric might not be a counter, name does not end in _total/_sum/_count:", PromQLInfo) +) + +type annoErr struct { + PositionRange posrange.PositionRange + Err error + Query string +} + +func (e annoErr) Error() string { + return fmt.Sprintf("%s (%s)", e.Err, e.PositionRange.StartPosInput(e.Query, 0)) +} + +// NewInvalidQuantileWarning is used when the user specifies an invalid quantile +// value, i.e. a float that is outside the range [0, 1] or NaN. +func NewInvalidQuantileWarning(q float64, pos posrange.PositionRange) annoErr { + return annoErr{ + PositionRange: pos, + Err: fmt.Errorf("%w, got %g", InvalidQuantileWarning, q), + } +} + +// NewBadBucketLabelWarning is used when there is an error parsing the bucket label +// of a classic histogram. +func NewBadBucketLabelWarning(metricName, label string, pos posrange.PositionRange) annoErr { + return annoErr{ + PositionRange: pos, + Err: fmt.Errorf("%w of %q for metric name %q", BadBucketLabelWarning, label, metricName), + } +} + +// NewMixedFloatsHistogramsWarning is used when the queried series includes both +// float samples and histogram samples for functions that do not support mixed +// samples. +func NewMixedFloatsHistogramsWarning(metricName string, pos posrange.PositionRange) annoErr { + return annoErr{ + PositionRange: pos, + Err: fmt.Errorf("%w %q", MixedFloatsHistogramsWarning, metricName), + } +} + +// NewMixedClassicNativeHistogramsWarning is used when the queried series includes +// both classic and native histograms. +func NewMixedClassicNativeHistogramsWarning(metricName string, pos posrange.PositionRange) annoErr { + return annoErr{ + PositionRange: pos, + Err: fmt.Errorf("%w %q", MixedClassicNativeHistogramsWarning, metricName), + } +} + +// NewPossibleNonCounterInfo is used when a counter metric does not have the suffixes +// _total, _sum or _count. +func NewPossibleNonCounterInfo(metricName string, pos posrange.PositionRange) annoErr { + return annoErr{ + PositionRange: pos, + Err: fmt.Errorf("%w %q", PossibleNonCounterInfo, metricName), + } +} diff --git a/web/api/v1/api.go b/web/api/v1/api.go index 15738da53..62a376b0b 100644 --- a/web/api/v1/api.go +++ b/web/api/v1/api.go @@ -51,6 +51,7 @@ import ( "github.com/prometheus/prometheus/storage/remote" "github.com/prometheus/prometheus/tsdb" "github.com/prometheus/prometheus/tsdb/index" + "github.com/prometheus/prometheus/util/annotations" "github.com/prometheus/prometheus/util/httputil" "github.com/prometheus/prometheus/util/stats" ) @@ -161,7 +162,7 @@ type Response struct { type apiFuncResult struct { data interface{} err *apiError - warnings storage.Warnings + warnings annotations.Annotations finalizer func() } @@ -337,7 +338,7 @@ func (api *API) Register(r *route.Router) { } if result.data != nil { - api.respond(w, r, result.data, result.warnings) + api.respond(w, r, result.data, result.warnings, r.FormValue("query")) return } w.WriteHeader(http.StatusNoContent) @@ -667,7 +668,7 @@ func (api *API) labelNames(r *http.Request) apiFuncResult { var ( names []string - warnings storage.Warnings + warnings annotations.Annotations ) if len(matcherSets) > 0 { labelNamesSet := make(map[string]struct{}) @@ -678,7 +679,7 @@ func (api *API) labelNames(r *http.Request) apiFuncResult { return apiFuncResult{nil, returnAPIError(err), warnings, nil} } - warnings = append(warnings, callWarnings...) + warnings.Merge(callWarnings) for _, val := range vals { labelNamesSet[val] = struct{}{} } @@ -743,17 +744,17 @@ func (api *API) labelValues(r *http.Request) (result apiFuncResult) { var ( vals []string - warnings storage.Warnings + warnings annotations.Annotations ) if len(matcherSets) > 0 { - var callWarnings storage.Warnings + var callWarnings annotations.Annotations labelValuesSet := make(map[string]struct{}) for _, matchers := range matcherSets { vals, callWarnings, err = q.LabelValues(ctx, name, matchers...) if err != nil { return apiFuncResult{nil, &apiError{errorExec, err}, warnings, closer} } - warnings = append(warnings, callWarnings...) + warnings.Merge(callWarnings) for _, val := range vals { labelValuesSet[val] = struct{}{} } @@ -1579,7 +1580,7 @@ func (api *API) serveWALReplayStatus(w http.ResponseWriter, r *http.Request) { Min: status.Min, Max: status.Max, Current: status.Current, - }, nil) + }, nil, "") } func (api *API) remoteRead(w http.ResponseWriter, r *http.Request) { @@ -1685,17 +1686,15 @@ func (api *API) cleanTombstones(*http.Request) apiFuncResult { return apiFuncResult{nil, nil, nil, nil} } -func (api *API) respond(w http.ResponseWriter, req *http.Request, data interface{}, warnings storage.Warnings) { +// Query string is needed to get the position information for the annotations, and it +// can be empty if the position information isn't needed. +func (api *API) respond(w http.ResponseWriter, req *http.Request, data interface{}, warnings annotations.Annotations, query string) { statusMessage := statusSuccess - var warningStrings []string - for _, warning := range warnings { - warningStrings = append(warningStrings, warning.Error()) - } resp := &Response{ Status: statusMessage, Data: data, - Warnings: warningStrings, + Warnings: warnings.AsStrings(query, 10), } codec, err := api.negotiateCodec(req, resp) diff --git a/web/api/v1/api_test.go b/web/api/v1/api_test.go index 799e0594f..c86165b78 100644 --- a/web/api/v1/api_test.go +++ b/web/api/v1/api_test.go @@ -2985,7 +2985,7 @@ func TestRespondSuccess(t *testing.T) { api.InstallCodec(&testCodec{contentType: MIMEType{"test", "can-encode-2"}, canEncode: true}) s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - api.respond(w, r, "test", nil) + api.respond(w, r, "test", nil, "") })) defer s.Close() @@ -3074,7 +3074,7 @@ func TestRespondSuccess_DefaultCodecCannotEncodeResponse(t *testing.T) { api.InstallCodec(&testCodec{contentType: MIMEType{"application", "default-format"}, canEncode: false}) s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - api.respond(w, r, "test", nil) + api.respond(w, r, "test", nil, "") })) defer s.Close() @@ -3473,7 +3473,7 @@ func BenchmarkRespond(b *testing.B) { api := API{} api.InstallCodec(JSONCodec{}) for n := 0; n < b.N; n++ { - api.respond(&testResponseWriter, request, c.response, nil) + api.respond(&testResponseWriter, request, c.response, nil, "") } }) } diff --git a/web/api/v1/errors_test.go b/web/api/v1/errors_test.go index bbe44a6f7..4673af201 100644 --- a/web/api/v1/errors_test.go +++ b/web/api/v1/errors_test.go @@ -36,6 +36,7 @@ import ( "github.com/prometheus/prometheus/rules" "github.com/prometheus/prometheus/scrape" "github.com/prometheus/prometheus/storage" + "github.com/prometheus/prometheus/util/annotations" ) func TestApiStatusCodes(t *testing.T) { @@ -170,11 +171,11 @@ type errorTestQuerier struct { err error } -func (t errorTestQuerier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, storage.Warnings, error) { +func (t errorTestQuerier) LabelValues(context.Context, string, ...*labels.Matcher) ([]string, annotations.Annotations, error) { return nil, nil, t.err } -func (t errorTestQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, storage.Warnings, error) { +func (t errorTestQuerier) LabelNames(context.Context, ...*labels.Matcher) ([]string, annotations.Annotations, error) { return nil, nil, t.err } @@ -205,7 +206,7 @@ func (t errorTestSeriesSet) Err() error { return t.err } -func (t errorTestSeriesSet) Warnings() storage.Warnings { +func (t errorTestSeriesSet) Warnings() annotations.Annotations { return nil }