mirror of https://github.com/prometheus/prometheus
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
984 lines
27 KiB
984 lines
27 KiB
// Copyright 2015 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 parser |
|
|
|
import ( |
|
"errors" |
|
"fmt" |
|
"math" |
|
"os" |
|
"runtime" |
|
"strconv" |
|
"strings" |
|
"sync" |
|
"time" |
|
|
|
"github.com/prometheus/common/model" |
|
|
|
"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" |
|
) |
|
|
|
var parserPool = sync.Pool{ |
|
New: func() interface{} { |
|
return &parser{} |
|
}, |
|
} |
|
|
|
type Parser interface { |
|
ParseExpr() (Expr, error) |
|
Close() |
|
} |
|
|
|
type parser struct { |
|
lex Lexer |
|
|
|
inject ItemType |
|
injecting bool |
|
|
|
// functions contains all functions supported by the parser instance. |
|
functions map[string]*Function |
|
|
|
// Everytime an Item is lexed that could be the end |
|
// of certain expressions its end position is stored here. |
|
lastClosing posrange.Pos |
|
|
|
yyParser yyParserImpl |
|
|
|
generatedParserResult interface{} |
|
parseErrors ParseErrors |
|
} |
|
|
|
type Opt func(p *parser) |
|
|
|
func WithFunctions(functions map[string]*Function) Opt { |
|
return func(p *parser) { |
|
p.functions = functions |
|
} |
|
} |
|
|
|
// NewParser returns a new parser. |
|
func NewParser(input string, opts ...Opt) *parser { //nolint:revive // unexported-return. |
|
p := parserPool.Get().(*parser) |
|
|
|
p.functions = Functions |
|
p.injecting = false |
|
p.parseErrors = nil |
|
p.generatedParserResult = nil |
|
|
|
// Clear lexer struct before reusing. |
|
p.lex = Lexer{ |
|
input: input, |
|
state: lexStatements, |
|
} |
|
|
|
// Apply user define options. |
|
for _, opt := range opts { |
|
opt(p) |
|
} |
|
|
|
return p |
|
} |
|
|
|
func (p *parser) ParseExpr() (expr Expr, err error) { |
|
defer p.recover(&err) |
|
|
|
parseResult := p.parseGenerated(START_EXPRESSION) |
|
|
|
if parseResult != nil { |
|
expr = parseResult.(Expr) |
|
} |
|
|
|
// Only typecheck when there are no syntax errors. |
|
if len(p.parseErrors) == 0 { |
|
p.checkAST(expr) |
|
} |
|
|
|
if len(p.parseErrors) != 0 { |
|
err = p.parseErrors |
|
} |
|
|
|
return expr, err |
|
} |
|
|
|
func (p *parser) Close() { |
|
defer parserPool.Put(p) |
|
} |
|
|
|
// ParseErr wraps a parsing error with line and position context. |
|
type ParseErr struct { |
|
PositionRange posrange.PositionRange |
|
Err error |
|
Query string |
|
|
|
// LineOffset is an additional line offset to be added. Only used inside unit tests. |
|
LineOffset int |
|
} |
|
|
|
func (e *ParseErr) Error() string { |
|
return fmt.Sprintf("%s: parse error: %s", e.PositionRange.StartPosInput(e.Query, e.LineOffset), e.Err) |
|
} |
|
|
|
type ParseErrors []ParseErr |
|
|
|
// Since producing multiple error messages might look weird when combined with error wrapping, |
|
// only the first error produced by the parser is included in the error string. |
|
// If getting the full error list is desired, it is recommended to typecast the error returned |
|
// by the parser to ParseErrors and work with the underlying slice. |
|
func (errs ParseErrors) Error() string { |
|
if len(errs) != 0 { |
|
return errs[0].Error() |
|
} |
|
// Should never happen |
|
// Panicking while printing an error seems like a bad idea, so the |
|
// situation is explained in the error message instead. |
|
return "error contains no error message" |
|
} |
|
|
|
// EnrichParseError enriches a single or list of parse errors (used for unit tests and promtool). |
|
func EnrichParseError(err error, enrich func(parseErr *ParseErr)) { |
|
var parseErr *ParseErr |
|
if errors.As(err, &parseErr) { |
|
enrich(parseErr) |
|
} |
|
var parseErrors ParseErrors |
|
if errors.As(err, &parseErrors) { |
|
for i, e := range parseErrors { |
|
enrich(&e) |
|
parseErrors[i] = e |
|
} |
|
} |
|
} |
|
|
|
// ParseExpr returns the expression parsed from the input. |
|
func ParseExpr(input string) (expr Expr, err error) { |
|
p := NewParser(input) |
|
defer p.Close() |
|
return p.ParseExpr() |
|
} |
|
|
|
// ParseMetric parses the input into a metric. |
|
func ParseMetric(input string) (m labels.Labels, err error) { |
|
p := NewParser(input) |
|
defer p.Close() |
|
defer p.recover(&err) |
|
|
|
parseResult := p.parseGenerated(START_METRIC) |
|
if parseResult != nil { |
|
m = parseResult.(labels.Labels) |
|
} |
|
|
|
if len(p.parseErrors) != 0 { |
|
err = p.parseErrors |
|
} |
|
|
|
return m, err |
|
} |
|
|
|
// ParseMetricSelector parses the provided textual metric selector into a list of |
|
// label matchers. |
|
func ParseMetricSelector(input string) (m []*labels.Matcher, err error) { |
|
p := NewParser(input) |
|
defer p.Close() |
|
defer p.recover(&err) |
|
|
|
parseResult := p.parseGenerated(START_METRIC_SELECTOR) |
|
if parseResult != nil { |
|
m = parseResult.(*VectorSelector).LabelMatchers |
|
} |
|
|
|
if len(p.parseErrors) != 0 { |
|
err = p.parseErrors |
|
} |
|
|
|
return m, err |
|
} |
|
|
|
// SequenceValue is an omittable value in a sequence of time series values. |
|
type SequenceValue struct { |
|
Value float64 |
|
Omitted bool |
|
Histogram *histogram.FloatHistogram |
|
} |
|
|
|
func (v SequenceValue) String() string { |
|
if v.Omitted { |
|
return "_" |
|
} |
|
if v.Histogram != nil { |
|
return v.Histogram.String() |
|
} |
|
return fmt.Sprintf("%f", v.Value) |
|
} |
|
|
|
type seriesDescription struct { |
|
labels labels.Labels |
|
values []SequenceValue |
|
} |
|
|
|
// ParseSeriesDesc parses the description of a time series. |
|
func ParseSeriesDesc(input string) (labels labels.Labels, values []SequenceValue, err error) { |
|
p := NewParser(input) |
|
p.lex.seriesDesc = true |
|
|
|
defer p.Close() |
|
defer p.recover(&err) |
|
|
|
parseResult := p.parseGenerated(START_SERIES_DESCRIPTION) |
|
if parseResult != nil { |
|
result := parseResult.(*seriesDescription) |
|
|
|
labels = result.labels |
|
values = result.values |
|
|
|
} |
|
|
|
if len(p.parseErrors) != 0 { |
|
err = p.parseErrors |
|
} |
|
|
|
return labels, values, err |
|
} |
|
|
|
// addParseErrf formats the error and appends it to the list of parsing errors. |
|
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 posrange.PositionRange, err error) { |
|
perr := ParseErr{ |
|
PositionRange: positionRange, |
|
Err: err, |
|
Query: p.lex.input, |
|
} |
|
|
|
p.parseErrors = append(p.parseErrors, perr) |
|
} |
|
|
|
func (p *parser) addSemanticError(err error) { |
|
p.addParseErr(p.yyParser.lval.item.PositionRange(), err) |
|
} |
|
|
|
// unexpected creates a parser error complaining about an unexpected lexer item. |
|
// The item that is presented as unexpected is always the last item produced |
|
// by the lexer. |
|
func (p *parser) unexpected(context, expected string) { |
|
var errMsg strings.Builder |
|
|
|
// Do not report lexer errors twice |
|
if p.yyParser.lval.item.Typ == ERROR { |
|
return |
|
} |
|
|
|
errMsg.WriteString("unexpected ") |
|
errMsg.WriteString(p.yyParser.lval.item.desc()) |
|
|
|
if context != "" { |
|
errMsg.WriteString(" in ") |
|
errMsg.WriteString(context) |
|
} |
|
|
|
if expected != "" { |
|
errMsg.WriteString(", expected ") |
|
errMsg.WriteString(expected) |
|
} |
|
|
|
p.addParseErr(p.yyParser.lval.item.PositionRange(), errors.New(errMsg.String())) |
|
} |
|
|
|
var errUnexpected = errors.New("unexpected error") |
|
|
|
// recover is the handler that turns panics into returns from the top level of Parse. |
|
func (p *parser) recover(errp *error) { |
|
e := recover() |
|
switch _, ok := e.(runtime.Error); { |
|
case ok: |
|
// Print the stack trace but do not inhibit the running application. |
|
buf := make([]byte, 64<<10) |
|
buf = buf[:runtime.Stack(buf, false)] |
|
|
|
fmt.Fprintf(os.Stderr, "parser panic: %v\n%s", e, buf) |
|
*errp = errUnexpected |
|
case e != nil: |
|
*errp = e.(error) |
|
} |
|
} |
|
|
|
// Lex is expected by the yyLexer interface of the yacc generated parser. |
|
// It writes the next Item provided by the lexer to the provided pointer address. |
|
// Comments are skipped. |
|
// |
|
// The yyLexer interface is currently implemented by the parser to allow |
|
// the generated and non-generated parts to work together with regards to lookahead |
|
// and error handling. |
|
// |
|
// For more information, see https://pkg.go.dev/golang.org/x/tools/cmd/goyacc. |
|
func (p *parser) Lex(lval *yySymType) int { |
|
var typ ItemType |
|
|
|
if p.injecting { |
|
p.injecting = false |
|
return int(p.inject) |
|
} |
|
// Skip comments. |
|
for { |
|
p.lex.NextItem(&lval.item) |
|
typ = lval.item.Typ |
|
if typ != COMMENT { |
|
break |
|
} |
|
} |
|
|
|
switch typ { |
|
case ERROR: |
|
pos := posrange.PositionRange{ |
|
Start: p.lex.start, |
|
End: posrange.Pos(len(p.lex.input)), |
|
} |
|
p.addParseErr(pos, errors.New(p.yyParser.lval.item.Val)) |
|
|
|
// Tells yacc that this is the end of input. |
|
return 0 |
|
case EOF: |
|
lval.item.Typ = EOF |
|
p.InjectItem(0) |
|
case RIGHT_BRACE, RIGHT_PAREN, RIGHT_BRACKET, DURATION, NUMBER: |
|
p.lastClosing = lval.item.Pos + posrange.Pos(len(lval.item.Val)) |
|
} |
|
|
|
return int(typ) |
|
} |
|
|
|
// Error is expected by the yyLexer interface of the yacc generated parser. |
|
// |
|
// It is a no-op since the parsers error routines are triggered |
|
// by mechanisms that allow more fine-grained control |
|
// For more information, see https://pkg.go.dev/golang.org/x/tools/cmd/goyacc. |
|
func (p *parser) Error(string) { |
|
} |
|
|
|
// InjectItem allows injecting a single Item at the beginning of the token stream |
|
// consumed by the generated parser. |
|
// This allows having multiple start symbols as described in |
|
// https://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html . |
|
// Only the Lex function used by the generated parser is affected by this injected Item. |
|
// Trying to inject when a previously injected Item has not yet been consumed will panic. |
|
// Only Item types that are supposed to be used as start symbols are allowed as an argument. |
|
func (p *parser) InjectItem(typ ItemType) { |
|
if p.injecting { |
|
panic("cannot inject multiple Items into the token stream") |
|
} |
|
|
|
if typ != 0 && (typ <= startSymbolsStart || typ >= startSymbolsEnd) { |
|
panic("cannot inject symbol that isn't start symbol") |
|
} |
|
|
|
p.inject = typ |
|
p.injecting = true |
|
} |
|
|
|
func (p *parser) newBinaryExpression(lhs Node, op Item, modifiers, rhs Node) *BinaryExpr { |
|
ret := modifiers.(*BinaryExpr) |
|
|
|
ret.LHS = lhs.(Expr) |
|
ret.RHS = rhs.(Expr) |
|
ret.Op = op.Typ |
|
|
|
return ret |
|
} |
|
|
|
func (p *parser) assembleVectorSelector(vs *VectorSelector) { |
|
if vs.Name != "" { |
|
nameMatcher, err := labels.NewMatcher(labels.MatchEqual, labels.MetricName, vs.Name) |
|
if err != nil { |
|
panic(err) // Must not happen with labels.MatchEqual |
|
} |
|
vs.LabelMatchers = append(vs.LabelMatchers, nameMatcher) |
|
} |
|
} |
|
|
|
func (p *parser) newAggregateExpr(op Item, modifier, args Node) (ret *AggregateExpr) { |
|
ret = modifier.(*AggregateExpr) |
|
arguments := args.(Expressions) |
|
|
|
ret.PosRange = posrange.PositionRange{ |
|
Start: op.Pos, |
|
End: p.lastClosing, |
|
} |
|
|
|
ret.Op = op.Typ |
|
|
|
if len(arguments) == 0 { |
|
p.addParseErrf(ret.PositionRange(), "no arguments for aggregate expression provided") |
|
|
|
// Prevents invalid array accesses. |
|
return |
|
} |
|
|
|
desiredArgs := 1 |
|
if ret.Op.IsAggregatorWithParam() { |
|
desiredArgs = 2 |
|
|
|
ret.Param = arguments[0] |
|
} |
|
|
|
if len(arguments) != desiredArgs { |
|
p.addParseErrf(ret.PositionRange(), "wrong number of arguments for aggregate expression provided, expected %d, got %d", desiredArgs, len(arguments)) |
|
return |
|
} |
|
|
|
ret.Expr = arguments[desiredArgs-1] |
|
|
|
return ret |
|
} |
|
|
|
// newMap is used when building the FloatHistogram from a map. |
|
func (p *parser) newMap() (ret map[string]interface{}) { |
|
return map[string]interface{}{} |
|
} |
|
|
|
// mergeMaps is used to combine maps as they're used to later build the Float histogram. |
|
// This will merge the right map into the left map. |
|
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(posrange.PositionRange{}, "duplicate key \"%s\" in histogram", key) |
|
continue |
|
} |
|
(*left)[key] = value |
|
} |
|
return left |
|
} |
|
|
|
func (p *parser) histogramsIncreaseSeries(base, inc *histogram.FloatHistogram, times uint64) ([]SequenceValue, error) { |
|
return p.histogramsSeries(base, inc, times, func(a, b *histogram.FloatHistogram) *histogram.FloatHistogram { |
|
return a.Add(b) |
|
}) |
|
} |
|
|
|
func (p *parser) histogramsDecreaseSeries(base, inc *histogram.FloatHistogram, times uint64) ([]SequenceValue, error) { |
|
return p.histogramsSeries(base, inc, times, func(a, b *histogram.FloatHistogram) *histogram.FloatHistogram { |
|
return a.Sub(b) |
|
}) |
|
} |
|
|
|
func (p *parser) histogramsSeries(base, inc *histogram.FloatHistogram, times uint64, |
|
combine func(*histogram.FloatHistogram, *histogram.FloatHistogram) *histogram.FloatHistogram, |
|
) ([]SequenceValue, error) { |
|
ret := make([]SequenceValue, times+1) |
|
// Add an additional value (the base) for time 0, which we ignore in tests. |
|
ret[0] = SequenceValue{Histogram: base} |
|
cur := base |
|
for i := uint64(1); i <= times; i++ { |
|
if cur.Schema > inc.Schema { |
|
return nil, fmt.Errorf("error combining histograms: cannot merge from schema %d to %d", inc.Schema, cur.Schema) |
|
} |
|
|
|
cur = combine(cur.Copy(), inc) |
|
ret[i] = SequenceValue{Histogram: cur} |
|
} |
|
|
|
return ret, nil |
|
} |
|
|
|
// buildHistogramFromMap is used in the grammar to take then individual parts of the histogram and complete it. |
|
func (p *parser) buildHistogramFromMap(desc *map[string]interface{}) *histogram.FloatHistogram { |
|
output := &histogram.FloatHistogram{} |
|
|
|
val, ok := (*desc)["schema"] |
|
if ok { |
|
schema, ok := val.(int64) |
|
if ok { |
|
output.Schema = int32(schema) |
|
} else { |
|
p.addParseErrf(p.yyParser.lval.item.PositionRange(), "error parsing schema number: %v", val) |
|
} |
|
} |
|
|
|
val, ok = (*desc)["sum"] |
|
if ok { |
|
sum, ok := val.(float64) |
|
if ok { |
|
output.Sum = sum |
|
} else { |
|
p.addParseErrf(p.yyParser.lval.item.PositionRange(), "error parsing sum number: %v", val) |
|
} |
|
} |
|
val, ok = (*desc)["count"] |
|
if ok { |
|
count, ok := val.(float64) |
|
if ok { |
|
output.Count = count |
|
} else { |
|
p.addParseErrf(p.yyParser.lval.item.PositionRange(), "error parsing count number: %v", val) |
|
} |
|
} |
|
|
|
val, ok = (*desc)["z_bucket"] |
|
if ok { |
|
bucket, ok := val.(float64) |
|
if ok { |
|
output.ZeroCount = bucket |
|
} else { |
|
p.addParseErrf(p.yyParser.lval.item.PositionRange(), "error parsing z_bucket number: %v", val) |
|
} |
|
} |
|
val, ok = (*desc)["z_bucket_w"] |
|
if ok { |
|
bucketWidth, ok := val.(float64) |
|
if ok { |
|
output.ZeroThreshold = bucketWidth |
|
} else { |
|
p.addParseErrf(p.yyParser.lval.item.PositionRange(), "error parsing z_bucket_w number: %v", val) |
|
} |
|
} |
|
|
|
buckets, spans := p.buildHistogramBucketsAndSpans(desc, "buckets", "offset") |
|
output.PositiveBuckets = buckets |
|
output.PositiveSpans = spans |
|
|
|
buckets, spans = p.buildHistogramBucketsAndSpans(desc, "n_buckets", "n_offset") |
|
output.NegativeBuckets = buckets |
|
output.NegativeSpans = spans |
|
|
|
return output |
|
} |
|
|
|
func (p *parser) buildHistogramBucketsAndSpans(desc *map[string]interface{}, bucketsKey, offsetKey string, |
|
) (buckets []float64, spans []histogram.Span) { |
|
bucketCount := 0 |
|
val, ok := (*desc)[bucketsKey] |
|
if ok { |
|
val, ok := val.([]float64) |
|
if ok { |
|
buckets = val |
|
bucketCount = len(buckets) |
|
} else { |
|
p.addParseErrf(p.yyParser.lval.item.PositionRange(), "error parsing %s float array: %v", bucketsKey, val) |
|
} |
|
} |
|
offset := int32(0) |
|
val, ok = (*desc)[offsetKey] |
|
if ok { |
|
val, ok := val.(int64) |
|
if ok { |
|
offset = int32(val) |
|
} else { |
|
p.addParseErrf(p.yyParser.lval.item.PositionRange(), "error parsing %s number: %v", offsetKey, val) |
|
} |
|
} |
|
if bucketCount > 0 { |
|
spans = []histogram.Span{{Offset: offset, Length: uint32(bucketCount)}} |
|
} |
|
return |
|
} |
|
|
|
// number parses a number. |
|
func (p *parser) number(val string) float64 { |
|
n, err := strconv.ParseInt(val, 0, 64) |
|
f := float64(n) |
|
if err != nil { |
|
f, err = strconv.ParseFloat(val, 64) |
|
} |
|
if err != nil { |
|
p.addParseErrf(p.yyParser.lval.item.PositionRange(), "error parsing number: %s", err) |
|
} |
|
return f |
|
} |
|
|
|
// expectType checks the type of the node and raises an error if it |
|
// is not of the expected type. |
|
func (p *parser) expectType(node Node, want ValueType, context string) { |
|
t := p.checkAST(node) |
|
if t != want { |
|
p.addParseErrf(node.PositionRange(), "expected type %s in %s, got %s", DocumentedType(want), context, DocumentedType(t)) |
|
} |
|
} |
|
|
|
// checkAST checks the validity of the provided AST. This includes type checking. |
|
func (p *parser) checkAST(node Node) (typ ValueType) { |
|
// For expressions the type is determined by their Type function. |
|
// Lists do not have a type but are not invalid either. |
|
switch n := node.(type) { |
|
case Expressions: |
|
typ = ValueTypeNone |
|
case Expr: |
|
typ = n.Type() |
|
default: |
|
p.addParseErrf(node.PositionRange(), "unknown node type: %T", node) |
|
} |
|
|
|
// Recursively check correct typing for child nodes and raise |
|
// errors in case of bad typing. |
|
switch n := node.(type) { |
|
case *EvalStmt: |
|
ty := p.checkAST(n.Expr) |
|
if ty == ValueTypeNone { |
|
p.addParseErrf(n.Expr.PositionRange(), "evaluation statement must have a valid expression type but got %s", DocumentedType(ty)) |
|
} |
|
|
|
case Expressions: |
|
for _, e := range n { |
|
ty := p.checkAST(e) |
|
if ty == ValueTypeNone { |
|
p.addParseErrf(e.PositionRange(), "expression must have a valid expression type but got %s", DocumentedType(ty)) |
|
} |
|
} |
|
case *AggregateExpr: |
|
if !n.Op.IsAggregator() { |
|
p.addParseErrf(n.PositionRange(), "aggregation operator expected in aggregation expression but got %q", n.Op) |
|
} |
|
p.expectType(n.Expr, ValueTypeVector, "aggregation expression") |
|
if n.Op == TOPK || n.Op == BOTTOMK || n.Op == QUANTILE { |
|
p.expectType(n.Param, ValueTypeScalar, "aggregation parameter") |
|
} |
|
if n.Op == COUNT_VALUES { |
|
p.expectType(n.Param, ValueTypeString, "aggregation parameter") |
|
} |
|
|
|
case *BinaryExpr: |
|
lt := p.checkAST(n.LHS) |
|
rt := p.checkAST(n.RHS) |
|
|
|
// 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 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++ { |
|
} |
|
for r.End = n.RHS.PositionRange().Start - 1; isSpace(rune(p.lex.input[r.End])); r.End-- { |
|
} |
|
return |
|
} |
|
|
|
if n.ReturnBool && !n.Op.IsComparisonOperator() { |
|
p.addParseErrf(opRange(), "bool modifier can only be used on comparison operators") |
|
} |
|
|
|
if n.Op.IsComparisonOperator() && !n.ReturnBool && n.RHS.Type() == ValueTypeScalar && n.LHS.Type() == ValueTypeScalar { |
|
p.addParseErrf(opRange(), "comparisons between scalars must use BOOL modifier") |
|
} |
|
|
|
if n.Op.IsSetOperator() && n.VectorMatching.Card == CardOneToOne { |
|
n.VectorMatching.Card = CardManyToMany |
|
} |
|
|
|
for _, l1 := range n.VectorMatching.MatchingLabels { |
|
for _, l2 := range n.VectorMatching.Include { |
|
if l1 == l2 && n.VectorMatching.On { |
|
p.addParseErrf(opRange(), "label %q must not occur in ON and GROUP clause at once", l1) |
|
} |
|
} |
|
} |
|
|
|
if !n.Op.IsOperator() { |
|
p.addParseErrf(n.PositionRange(), "binary expression does not support operator %q", n.Op) |
|
} |
|
if lt != ValueTypeScalar && lt != ValueTypeVector { |
|
p.addParseErrf(n.LHS.PositionRange(), "binary expression must contain only scalar and instant vector types") |
|
} |
|
if rt != ValueTypeScalar && rt != ValueTypeVector { |
|
p.addParseErrf(n.RHS.PositionRange(), "binary expression must contain only scalar and instant vector types") |
|
} |
|
|
|
switch { |
|
case (lt != ValueTypeVector || rt != ValueTypeVector) && n.VectorMatching != nil: |
|
if len(n.VectorMatching.MatchingLabels) > 0 { |
|
p.addParseErrf(n.PositionRange(), "vector matching only allowed between instant vectors") |
|
} |
|
n.VectorMatching = nil |
|
case n.Op.IsSetOperator(): // Both operands are Vectors. |
|
if n.VectorMatching.Card == CardOneToMany || n.VectorMatching.Card == CardManyToOne { |
|
p.addParseErrf(n.PositionRange(), "no grouping allowed for %q operation", n.Op) |
|
} |
|
if n.VectorMatching.Card != CardManyToMany { |
|
p.addParseErrf(n.PositionRange(), "set operations must always be many-to-many") |
|
} |
|
} |
|
|
|
if (lt == ValueTypeScalar || rt == ValueTypeScalar) && n.Op.IsSetOperator() { |
|
p.addParseErrf(n.PositionRange(), "set operator %q not allowed in binary scalar expression", n.Op) |
|
} |
|
|
|
case *Call: |
|
nargs := len(n.Func.ArgTypes) |
|
if n.Func.Variadic == 0 { |
|
if nargs != len(n.Args) { |
|
p.addParseErrf(n.PositionRange(), "expected %d argument(s) in call to %q, got %d", nargs, n.Func.Name, len(n.Args)) |
|
} |
|
} else { |
|
na := nargs - 1 |
|
if na > len(n.Args) { |
|
p.addParseErrf(n.PositionRange(), "expected at least %d argument(s) in call to %q, got %d", na, n.Func.Name, len(n.Args)) |
|
} else if nargsmax := na + n.Func.Variadic; n.Func.Variadic > 0 && nargsmax < len(n.Args) { |
|
p.addParseErrf(n.PositionRange(), "expected at most %d argument(s) in call to %q, got %d", nargsmax, n.Func.Name, len(n.Args)) |
|
} |
|
} |
|
|
|
for i, arg := range n.Args { |
|
if i >= len(n.Func.ArgTypes) { |
|
if n.Func.Variadic == 0 { |
|
// This is not a vararg function so we should not check the |
|
// type of the extra arguments. |
|
break |
|
} |
|
i = len(n.Func.ArgTypes) - 1 |
|
} |
|
p.expectType(arg, n.Func.ArgTypes[i], fmt.Sprintf("call to function %q", n.Func.Name)) |
|
} |
|
|
|
case *ParenExpr: |
|
p.checkAST(n.Expr) |
|
|
|
case *UnaryExpr: |
|
if n.Op != ADD && n.Op != SUB { |
|
p.addParseErrf(n.PositionRange(), "only + and - operators allowed for unary expressions") |
|
} |
|
if t := p.checkAST(n.Expr); t != ValueTypeScalar && t != ValueTypeVector { |
|
p.addParseErrf(n.PositionRange(), "unary expression only allowed on expressions of type scalar or instant vector, got %q", DocumentedType(t)) |
|
} |
|
|
|
case *SubqueryExpr: |
|
ty := p.checkAST(n.Expr) |
|
if ty != ValueTypeVector { |
|
p.addParseErrf(n.PositionRange(), "subquery is only allowed on instant vector, got %s instead", ty) |
|
} |
|
case *MatrixSelector: |
|
p.checkAST(n.VectorSelector) |
|
|
|
case *VectorSelector: |
|
if n.Name != "" { |
|
// In this case the last LabelMatcher is checking for the metric name |
|
// set outside the braces. This checks if the name has already been set |
|
// previously. |
|
for _, m := range n.LabelMatchers[0 : len(n.LabelMatchers)-1] { |
|
if m != nil && m.Name == labels.MetricName { |
|
p.addParseErrf(n.PositionRange(), "metric name must not be set twice: %q or %q", n.Name, m.Value) |
|
} |
|
} |
|
|
|
// Skip the check for non-empty matchers because an explicit |
|
// metric name is a non-empty matcher. |
|
break |
|
} |
|
|
|
// A Vector selector must contain at least one non-empty matcher to prevent |
|
// implicit selection of all metrics (e.g. by a typo). |
|
notEmpty := false |
|
for _, lm := range n.LabelMatchers { |
|
if lm != nil && !lm.Matches("") { |
|
notEmpty = true |
|
break |
|
} |
|
} |
|
if !notEmpty { |
|
p.addParseErrf(n.PositionRange(), "vector selector must contain at least one non-empty matcher") |
|
} |
|
|
|
case *NumberLiteral, *StringLiteral: |
|
// Nothing to do for terminals. |
|
|
|
default: |
|
p.addParseErrf(n.PositionRange(), "unknown node type: %T", node) |
|
} |
|
return |
|
} |
|
|
|
func (p *parser) unquoteString(s string) string { |
|
unquoted, err := strutil.Unquote(s) |
|
if err != nil { |
|
p.addParseErrf(p.yyParser.lval.item.PositionRange(), "error unquoting string %q: %s", s, err) |
|
} |
|
return unquoted |
|
} |
|
|
|
func parseDuration(ds string) (time.Duration, error) { |
|
dur, err := model.ParseDuration(ds) |
|
if err != nil { |
|
return 0, err |
|
} |
|
if dur == 0 { |
|
return 0, errors.New("duration must be greater than 0") |
|
} |
|
return time.Duration(dur), nil |
|
} |
|
|
|
// parseGenerated invokes the yacc generated parser. |
|
// The generated parser gets the provided startSymbol injected into |
|
// the lexer stream, based on which grammar will be used. |
|
func (p *parser) parseGenerated(startSymbol ItemType) interface{} { |
|
p.InjectItem(startSymbol) |
|
|
|
p.yyParser.Parse(p) |
|
|
|
return p.generatedParserResult |
|
} |
|
|
|
func (p *parser) newLabelMatcher(label, operator, value Item) *labels.Matcher { |
|
op := operator.Typ |
|
val := p.unquoteString(value.Val) |
|
|
|
// Map the Item to the respective match type. |
|
var matchType labels.MatchType |
|
switch op { |
|
case EQL: |
|
matchType = labels.MatchEqual |
|
case NEQ: |
|
matchType = labels.MatchNotEqual |
|
case EQL_REGEX: |
|
matchType = labels.MatchRegexp |
|
case NEQ_REGEX: |
|
matchType = labels.MatchNotRegexp |
|
default: |
|
// This should never happen, since the error should have been caught |
|
// by the generated parser. |
|
panic("invalid operator") |
|
} |
|
|
|
m, err := labels.NewMatcher(matchType, label.Val, val) |
|
if err != nil { |
|
p.addParseErr(mergeRanges(&label, &value), err) |
|
} |
|
|
|
return m |
|
} |
|
|
|
// 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 *posrange.Pos |
|
|
|
switch s := e.(type) { |
|
case *VectorSelector: |
|
orgoffsetp = &s.OriginalOffset |
|
endPosp = &s.PosRange.End |
|
case *MatrixSelector: |
|
vs, ok := s.VectorSelector.(*VectorSelector) |
|
if !ok { |
|
p.addParseErrf(e.PositionRange(), "ranges only allowed for vector selectors") |
|
return |
|
} |
|
orgoffsetp = &vs.OriginalOffset |
|
endPosp = &s.EndPos |
|
case *SubqueryExpr: |
|
orgoffsetp = &s.OriginalOffset |
|
endPosp = &s.EndPos |
|
default: |
|
p.addParseErrf(e.PositionRange(), "offset modifier must be preceded by an instant vector selector or range vector selector or a subquery") |
|
return |
|
} |
|
|
|
// it is already ensured by parseDuration func that there never will be a zero offset modifier |
|
switch { |
|
case *orgoffsetp != 0: |
|
p.addParseErrf(e.PositionRange(), "offset may not be set multiple times") |
|
case orgoffsetp != nil: |
|
*orgoffsetp = offset |
|
} |
|
|
|
*endPosp = p.lastClosing |
|
} |
|
|
|
// setTimestamp is used to set the timestamp from the @ modifier in the generated parser. |
|
func (p *parser) setTimestamp(e Node, ts float64) { |
|
if math.IsInf(ts, -1) || math.IsInf(ts, 1) || math.IsNaN(ts) || |
|
ts >= float64(math.MaxInt64) || ts <= float64(math.MinInt64) { |
|
p.addParseErrf(e.PositionRange(), "timestamp out of bounds for @ modifier: %f", ts) |
|
} |
|
var timestampp **int64 |
|
var endPosp *posrange.Pos |
|
|
|
timestampp, _, endPosp, ok := p.getAtModifierVars(e) |
|
if !ok { |
|
return |
|
} |
|
|
|
if timestampp != nil { |
|
*timestampp = new(int64) |
|
**timestampp = timestamp.FromFloatSeconds(ts) |
|
} |
|
|
|
*endPosp = p.lastClosing |
|
} |
|
|
|
// setAtModifierPreprocessor is used to set the preprocessor for the @ modifier. |
|
func (p *parser) setAtModifierPreprocessor(e Node, op Item) { |
|
_, preprocp, endPosp, ok := p.getAtModifierVars(e) |
|
if !ok { |
|
return |
|
} |
|
|
|
if preprocp != nil { |
|
*preprocp = op.Typ |
|
} |
|
|
|
*endPosp = p.lastClosing |
|
} |
|
|
|
func (p *parser) getAtModifierVars(e Node) (**int64, *ItemType, *posrange.Pos, bool) { |
|
var ( |
|
timestampp **int64 |
|
preprocp *ItemType |
|
endPosp *posrange.Pos |
|
) |
|
switch s := e.(type) { |
|
case *VectorSelector: |
|
timestampp = &s.Timestamp |
|
preprocp = &s.StartOrEnd |
|
endPosp = &s.PosRange.End |
|
case *MatrixSelector: |
|
vs, ok := s.VectorSelector.(*VectorSelector) |
|
if !ok { |
|
p.addParseErrf(e.PositionRange(), "ranges only allowed for vector selectors") |
|
return nil, nil, nil, false |
|
} |
|
preprocp = &vs.StartOrEnd |
|
timestampp = &vs.Timestamp |
|
endPosp = &s.EndPos |
|
case *SubqueryExpr: |
|
preprocp = &s.StartOrEnd |
|
timestampp = &s.Timestamp |
|
endPosp = &s.EndPos |
|
default: |
|
p.addParseErrf(e.PositionRange(), "@ modifier must be preceded by an instant vector selector or range vector selector or a subquery") |
|
return nil, nil, nil, false |
|
} |
|
|
|
if *timestampp != nil || (*preprocp) == START || (*preprocp) == END { |
|
p.addParseErrf(e.PositionRange(), "@ <timestamp> may not be set multiple times") |
|
return nil, nil, nil, false |
|
} |
|
|
|
return timestampp, preprocp, endPosp, true |
|
} |
|
|
|
func MustLabelMatcher(mt labels.MatchType, name, val string) *labels.Matcher { |
|
m, err := labels.NewMatcher(mt, name, val) |
|
if err != nil { |
|
panic(err) |
|
} |
|
return m |
|
} |
|
|
|
func MustGetFunction(name string) *Function { |
|
f, ok := getFunction(name, Functions) |
|
if !ok { |
|
panic(fmt.Errorf("function %q does not exist", name)) |
|
} |
|
return f |
|
}
|
|
|