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.
492 lines
14 KiB
492 lines
14 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 ( |
|
"context" |
|
"fmt" |
|
"time" |
|
|
|
"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. |
|
// |
|
// Whenever numerous nodes are listed such as in a switch-case statement |
|
// or a chain of function definitions (e.g. String(), PromQLExpr(), etc.) convention is |
|
// to list them as follows: |
|
// |
|
// - Statements |
|
// - statement types (alphabetical) |
|
// - ... |
|
// - Expressions |
|
// - expression types (alphabetical) |
|
// - ... |
|
type Node interface { |
|
// String representation of the node that returns the given node when parsed |
|
// as part of a valid query. |
|
String() string |
|
|
|
// Pretty returns the prettified representation of the node. |
|
// It uses the level information to determine at which level/depth the current |
|
// node is in the AST and uses this to apply indentation. |
|
Pretty(level int) string |
|
|
|
// PositionRange returns the position of the AST Node in the query string. |
|
PositionRange() posrange.PositionRange |
|
} |
|
|
|
// Statement is a generic interface for all statements. |
|
type Statement interface { |
|
Node |
|
|
|
// PromQLStmt ensures that no other type accidentally implements the interface |
|
PromQLStmt() |
|
} |
|
|
|
// EvalStmt holds an expression and information on the range it should |
|
// be evaluated on. |
|
type EvalStmt struct { |
|
Expr Expr // Expression to be evaluated. |
|
|
|
// The time boundaries for the evaluation. If Start equals End an instant |
|
// is evaluated. |
|
Start, End time.Time |
|
// Time between two evaluated instants for the range [Start:End]. |
|
Interval time.Duration |
|
// Lookback delta to use for this evaluation. |
|
LookbackDelta time.Duration |
|
} |
|
|
|
func (*EvalStmt) PromQLStmt() {} |
|
|
|
// Expr is a generic interface for all expression types. |
|
type Expr interface { |
|
Node |
|
|
|
// Type returns the type the expression evaluates to. It does not perform |
|
// in-depth checks as this is done at parsing-time. |
|
Type() ValueType |
|
// PromQLExpr ensures that no other types accidentally implement the interface. |
|
PromQLExpr() |
|
} |
|
|
|
// Expressions is a list of expression nodes that implements Node. |
|
type Expressions []Expr |
|
|
|
// AggregateExpr represents an aggregation operation on a Vector. |
|
type AggregateExpr struct { |
|
Op ItemType // The used aggregation operation. |
|
Expr Expr // The Vector expression over which is aggregated. |
|
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 posrange.PositionRange |
|
} |
|
|
|
// BinaryExpr represents a binary expression between two child expressions. |
|
type BinaryExpr struct { |
|
Op ItemType // The operation of the expression. |
|
LHS, RHS Expr // The operands on the respective sides of the operator. |
|
|
|
// The matching behavior for the operation if both operands are Vectors. |
|
// If they are not this field is nil. |
|
VectorMatching *VectorMatching |
|
|
|
// If a comparison operator, return 0/1 rather than filtering. |
|
ReturnBool bool |
|
} |
|
|
|
// Call represents a function call. |
|
type Call struct { |
|
Func *Function // The function that was called. |
|
Args Expressions // Arguments used in the call. |
|
|
|
PosRange posrange.PositionRange |
|
} |
|
|
|
// MatrixSelector represents a Matrix selection. |
|
type MatrixSelector struct { |
|
// It is safe to assume that this is an VectorSelector |
|
// if the parser hasn't returned an error. |
|
VectorSelector Expr |
|
Range time.Duration |
|
|
|
EndPos posrange.Pos |
|
} |
|
|
|
// SubqueryExpr represents a subquery. |
|
type SubqueryExpr struct { |
|
Expr Expr |
|
Range time.Duration |
|
// OriginalOffset is the actual offset that was set in the query. |
|
// This never changes. |
|
OriginalOffset time.Duration |
|
// Offset is the offset used during the query execution |
|
// which is calculated using the original offset, at modifier time, |
|
// eval time, and subquery offsets in the AST tree. |
|
Offset time.Duration |
|
Timestamp *int64 |
|
StartOrEnd ItemType // Set when @ is used with start() or end() |
|
Step time.Duration |
|
|
|
EndPos posrange.Pos |
|
} |
|
|
|
// NumberLiteral represents a number. |
|
type NumberLiteral struct { |
|
Val float64 |
|
|
|
PosRange posrange.PositionRange |
|
} |
|
|
|
// ParenExpr wraps an expression so it cannot be disassembled as a consequence |
|
// of operator precedence. |
|
type ParenExpr struct { |
|
Expr Expr |
|
PosRange posrange.PositionRange |
|
} |
|
|
|
// StringLiteral represents a string. |
|
type StringLiteral struct { |
|
Val string |
|
PosRange posrange.PositionRange |
|
} |
|
|
|
// UnaryExpr represents a unary operation on another expression. |
|
// Currently unary operations are only supported for Scalars. |
|
type UnaryExpr struct { |
|
Op ItemType |
|
Expr Expr |
|
|
|
StartPos posrange.Pos |
|
} |
|
|
|
// StepInvariantExpr represents a query which evaluates to the same result |
|
// irrespective of the evaluation time given the raw samples from TSDB remain unchanged. |
|
// Currently this is only used for engine optimisations and the parser does not produce this. |
|
type StepInvariantExpr struct { |
|
Expr Expr |
|
} |
|
|
|
func (e *StepInvariantExpr) String() string { return e.Expr.String() } |
|
|
|
func (e *StepInvariantExpr) PositionRange() posrange.PositionRange { |
|
return e.Expr.PositionRange() |
|
} |
|
|
|
// VectorSelector represents a Vector selection. |
|
type VectorSelector struct { |
|
Name string |
|
// OriginalOffset is the actual offset that was set in the query. |
|
// This never changes. |
|
OriginalOffset time.Duration |
|
// Offset is the offset used during the query execution |
|
// which is calculated using the original offset, at modifier time, |
|
// eval time, and subquery offsets in the AST tree. |
|
Offset time.Duration |
|
Timestamp *int64 |
|
StartOrEnd ItemType // Set when @ is used with start() or end() |
|
LabelMatchers []*labels.Matcher |
|
|
|
// The unexpanded seriesSet populated at query preparation time. |
|
UnexpandedSeriesSet storage.SeriesSet |
|
Series []storage.Series |
|
|
|
PosRange posrange.PositionRange |
|
} |
|
|
|
// TestStmt is an internal helper statement that allows execution |
|
// of an arbitrary function during handling. It is used to test the Engine. |
|
type TestStmt func(context.Context) error |
|
|
|
func (TestStmt) String() string { return "test statement" } |
|
func (TestStmt) PromQLStmt() {} |
|
func (t TestStmt) Pretty(int) string { return t.String() } |
|
|
|
func (TestStmt) PositionRange() posrange.PositionRange { |
|
return posrange.PositionRange{ |
|
Start: -1, |
|
End: -1, |
|
} |
|
} |
|
func (e *AggregateExpr) Type() ValueType { return ValueTypeVector } |
|
func (e *Call) Type() ValueType { return e.Func.ReturnType } |
|
func (e *MatrixSelector) Type() ValueType { return ValueTypeMatrix } |
|
func (e *SubqueryExpr) Type() ValueType { return ValueTypeMatrix } |
|
func (e *NumberLiteral) Type() ValueType { return ValueTypeScalar } |
|
func (e *ParenExpr) Type() ValueType { return e.Expr.Type() } |
|
func (e *StringLiteral) Type() ValueType { return ValueTypeString } |
|
func (e *UnaryExpr) Type() ValueType { return e.Expr.Type() } |
|
func (e *VectorSelector) Type() ValueType { return ValueTypeVector } |
|
func (e *BinaryExpr) Type() ValueType { |
|
if e.LHS.Type() == ValueTypeScalar && e.RHS.Type() == ValueTypeScalar { |
|
return ValueTypeScalar |
|
} |
|
return ValueTypeVector |
|
} |
|
func (e *StepInvariantExpr) Type() ValueType { return e.Expr.Type() } |
|
|
|
func (*AggregateExpr) PromQLExpr() {} |
|
func (*BinaryExpr) PromQLExpr() {} |
|
func (*Call) PromQLExpr() {} |
|
func (*MatrixSelector) PromQLExpr() {} |
|
func (*SubqueryExpr) PromQLExpr() {} |
|
func (*NumberLiteral) PromQLExpr() {} |
|
func (*ParenExpr) PromQLExpr() {} |
|
func (*StringLiteral) PromQLExpr() {} |
|
func (*UnaryExpr) PromQLExpr() {} |
|
func (*VectorSelector) PromQLExpr() {} |
|
func (*StepInvariantExpr) PromQLExpr() {} |
|
|
|
// VectorMatchCardinality describes the cardinality relationship |
|
// of two Vectors in a binary operation. |
|
type VectorMatchCardinality int |
|
|
|
const ( |
|
CardOneToOne VectorMatchCardinality = iota |
|
CardManyToOne |
|
CardOneToMany |
|
CardManyToMany |
|
) |
|
|
|
func (vmc VectorMatchCardinality) String() string { |
|
switch vmc { |
|
case CardOneToOne: |
|
return "one-to-one" |
|
case CardManyToOne: |
|
return "many-to-one" |
|
case CardOneToMany: |
|
return "one-to-many" |
|
case CardManyToMany: |
|
return "many-to-many" |
|
} |
|
panic("promql.VectorMatchCardinality.String: unknown match cardinality") |
|
} |
|
|
|
// VectorMatching describes how elements from two Vectors in a binary |
|
// operation are supposed to be matched. |
|
type VectorMatching struct { |
|
// The cardinality of the two Vectors. |
|
Card VectorMatchCardinality |
|
// MatchingLabels contains the labels which define equality of a pair of |
|
// elements from the Vectors. |
|
MatchingLabels []string |
|
// On includes the given label names from matching, |
|
// rather than excluding them. |
|
On bool |
|
// Include contains additional labels that should be included in |
|
// the result from the side with the lower cardinality. |
|
Include []string |
|
} |
|
|
|
// Visitor allows visiting a Node and its child nodes. The Visit method is |
|
// invoked for each node with the path leading to the node provided additionally. |
|
// If the result visitor w is not nil and no error, Walk visits each of the children |
|
// of node with the visitor w, followed by a call of w.Visit(nil, nil). |
|
type Visitor interface { |
|
Visit(node Node, path []Node) (w Visitor, err error) |
|
} |
|
|
|
// Walk traverses an AST in depth-first order: It starts by calling |
|
// v.Visit(node, path); node must not be nil. If the visitor w returned by |
|
// v.Visit(node, path) is not nil and the visitor returns no error, Walk is |
|
// invoked recursively with visitor w for each of the non-nil children of node, |
|
// followed by a call of w.Visit(nil), returning an error |
|
// As the tree is descended the path of previous nodes is provided. |
|
func Walk(v Visitor, node Node, path []Node) error { |
|
var err error |
|
if v, err = v.Visit(node, path); v == nil || err != nil { |
|
return err |
|
} |
|
path = append(path, node) |
|
|
|
for _, e := range Children(node) { |
|
if err := Walk(v, e, path); err != nil { |
|
return err |
|
} |
|
} |
|
|
|
_, err = v.Visit(nil, nil) |
|
return err |
|
} |
|
|
|
func ExtractSelectors(expr Expr) [][]*labels.Matcher { |
|
var selectors [][]*labels.Matcher |
|
Inspect(expr, func(node Node, _ []Node) error { |
|
vs, ok := node.(*VectorSelector) |
|
if ok { |
|
selectors = append(selectors, vs.LabelMatchers) |
|
} |
|
return nil |
|
}) |
|
return selectors |
|
} |
|
|
|
type inspector func(Node, []Node) error |
|
|
|
func (f inspector) Visit(node Node, path []Node) (Visitor, error) { |
|
if err := f(node, path); err != nil { |
|
return nil, err |
|
} |
|
|
|
return f, nil |
|
} |
|
|
|
// Inspect traverses an AST in depth-first order: It starts by calling |
|
// f(node, path); node must not be nil. If f returns a nil error, Inspect invokes f |
|
// for all the non-nil children of node, recursively. |
|
func Inspect(node Node, f inspector) { |
|
//nolint: errcheck |
|
Walk(f, node, nil) |
|
} |
|
|
|
// Children returns a list of all child nodes of a syntax tree node. |
|
func Children(node Node) []Node { |
|
// For some reasons these switches have significantly better performance than interfaces |
|
switch n := node.(type) { |
|
case *EvalStmt: |
|
return []Node{n.Expr} |
|
case Expressions: |
|
// golang cannot convert slices of interfaces |
|
ret := make([]Node, len(n)) |
|
for i, e := range n { |
|
ret[i] = e |
|
} |
|
return ret |
|
case *AggregateExpr: |
|
// While this does not look nice, it should avoid unnecessary allocations |
|
// caused by slice resizing |
|
switch { |
|
case n.Expr == nil && n.Param == nil: |
|
return nil |
|
case n.Expr == nil: |
|
return []Node{n.Param} |
|
case n.Param == nil: |
|
return []Node{n.Expr} |
|
default: |
|
return []Node{n.Expr, n.Param} |
|
} |
|
case *BinaryExpr: |
|
return []Node{n.LHS, n.RHS} |
|
case *Call: |
|
// golang cannot convert slices of interfaces |
|
ret := make([]Node, len(n.Args)) |
|
for i, e := range n.Args { |
|
ret[i] = e |
|
} |
|
return ret |
|
case *SubqueryExpr: |
|
return []Node{n.Expr} |
|
case *ParenExpr: |
|
return []Node{n.Expr} |
|
case *UnaryExpr: |
|
return []Node{n.Expr} |
|
case *MatrixSelector: |
|
return []Node{n.VectorSelector} |
|
case *StepInvariantExpr: |
|
return []Node{n.Expr} |
|
case *NumberLiteral, *StringLiteral, *VectorSelector: |
|
// nothing to do |
|
return []Node{} |
|
default: |
|
panic(fmt.Errorf("promql.Children: unhandled node type %T", node)) |
|
} |
|
} |
|
|
|
// 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) posrange.PositionRange { |
|
return posrange.PositionRange{ |
|
Start: first.PositionRange().Start, |
|
End: last.PositionRange().End, |
|
} |
|
} |
|
|
|
// Item implements the Node interface. |
|
// This makes it possible to call mergeRanges on them. |
|
func (i *Item) PositionRange() posrange.PositionRange { |
|
return posrange.PositionRange{ |
|
Start: i.Pos, |
|
End: i.Pos + posrange.Pos(len(i.Val)), |
|
} |
|
} |
|
|
|
func (e *AggregateExpr) PositionRange() posrange.PositionRange { |
|
return e.PosRange |
|
} |
|
|
|
func (e *BinaryExpr) PositionRange() posrange.PositionRange { |
|
return mergeRanges(e.LHS, e.RHS) |
|
} |
|
|
|
func (e *Call) PositionRange() posrange.PositionRange { |
|
return e.PosRange |
|
} |
|
|
|
func (e *EvalStmt) PositionRange() posrange.PositionRange { |
|
return e.Expr.PositionRange() |
|
} |
|
|
|
func (e Expressions) PositionRange() posrange.PositionRange { |
|
if len(e) == 0 { |
|
// Position undefined. |
|
return posrange.PositionRange{ |
|
Start: -1, |
|
End: -1, |
|
} |
|
} |
|
return mergeRanges(e[0], e[len(e)-1]) |
|
} |
|
|
|
func (e *MatrixSelector) PositionRange() posrange.PositionRange { |
|
return posrange.PositionRange{ |
|
Start: e.VectorSelector.PositionRange().Start, |
|
End: e.EndPos, |
|
} |
|
} |
|
|
|
func (e *SubqueryExpr) PositionRange() posrange.PositionRange { |
|
return posrange.PositionRange{ |
|
Start: e.Expr.PositionRange().Start, |
|
End: e.EndPos, |
|
} |
|
} |
|
|
|
func (e *NumberLiteral) PositionRange() posrange.PositionRange { |
|
return e.PosRange |
|
} |
|
|
|
func (e *ParenExpr) PositionRange() posrange.PositionRange { |
|
return e.PosRange |
|
} |
|
|
|
func (e *StringLiteral) PositionRange() posrange.PositionRange { |
|
return e.PosRange |
|
} |
|
|
|
func (e *UnaryExpr) PositionRange() posrange.PositionRange { |
|
return posrange.PositionRange{ |
|
Start: e.StartPos, |
|
End: e.Expr.PositionRange().End, |
|
} |
|
} |
|
|
|
func (e *VectorSelector) PositionRange() posrange.PositionRange { |
|
return e.PosRange |
|
}
|
|
|