promql: misc fixes

This commit is contained in:
Fabian Reinartz 2016-12-28 09:16:48 +01:00
parent fecf9532b9
commit 71fe0c58a8
11 changed files with 394 additions and 344 deletions

View file

@ -205,7 +205,9 @@ func (b *Builder) Labels() Labels {
return b.base return b.base
} }
res := make(Labels, 0, len(b.base)+len(b.add)-len(b.del)) // In the general case, labels are removed, modified or moved
// rather than added.
res := make(Labels, 0, len(b.base))
Outer: Outer:
for _, l := range b.base { for _, l := range b.base {
for _, n := range b.del { for _, n := range b.del {

View file

@ -184,7 +184,7 @@ func (e *BinaryExpr) Type() ValueType {
if e.LHS.Type() == ValueTypeScalar && e.RHS.Type() == ValueTypeScalar { if e.LHS.Type() == ValueTypeScalar && e.RHS.Type() == ValueTypeScalar {
return ValueTypeScalar return ValueTypeScalar
} }
return ValueTypeScalar return ValueTypeVector
} }
func (*AggregateExpr) expr() {} func (*AggregateExpr) expr() {}

View file

@ -19,11 +19,12 @@ import (
"math" "math"
"runtime" "runtime"
"sort" "sort"
"strings" "strconv"
"time" "time"
"github.com/prometheus/common/log" "github.com/prometheus/common/log"
"github.com/prometheus/prometheus/pkg/labels" "github.com/prometheus/prometheus/pkg/labels"
"github.com/prometheus/prometheus/pkg/timestamp"
"github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/storage"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -42,163 +43,6 @@ func convertibleToInt64(v float64) bool {
return v <= maxInt64 && v >= minInt64 return v <= maxInt64 && v >= minInt64
} }
// Value is a generic interface for values resulting from a query evaluation.
type Value interface {
Type() ValueType
String() string
}
func (Matrix) Type() ValueType { return ValueTypeMatrix }
func (Vector) Type() ValueType { return ValueTypeVector }
func (Scalar) Type() ValueType { return ValueTypeScalar }
func (String) Type() ValueType { return ValueTypeString }
// ValueType describes a type of a value.
type ValueType string
// The valid value types.
const (
ValueTypeNone = "none"
ValueTypeVector = "vector"
ValueTypeScalar = "scalar"
ValueTypeMatrix = "matrix"
ValueTypeString = "string"
)
// String represents a string value.
type String struct {
V string
T int64
}
func (s String) String() string {
return s.V
}
// Scalar is a data point that's explicitly not associated with a metric.
type Scalar struct {
T int64
V float64
}
func (s Scalar) String() string {
return ""
}
// Series is a stream of data points belonging to a metric.
type Series struct {
Metric labels.Labels
Points []Point
}
func (s Series) String() string {
return ""
}
// Point represents a single data point for a given timestamp.
type Point struct {
T int64
V float64
}
func (s Point) String() string {
return ""
}
// Sample is a single sample belonging to a metric.
type Sample struct {
Point
Metric labels.Labels
}
func (s Sample) String() string {
return ""
}
// Vector is basically only an alias for model.Samples, but the
// contract is that in a Vector, all Samples have the same timestamp.
type Vector []Sample
func (vec Vector) String() string {
entries := make([]string, len(vec))
for i, s := range vec {
entries[i] = s.String()
}
return strings.Join(entries, "\n")
}
// Matrix is a slice of Seriess that implements sort.Interface and
// has a String method.
type Matrix []Series
func (m Matrix) String() string {
// TODO(fabxc): sort, or can we rely on order from the querier?
strs := make([]string, len(m))
for i, ss := range m {
strs[i] = ss.String()
}
return strings.Join(strs, "\n")
}
// Result holds the resulting value of an execution or an error
// if any occurred.
type Result struct {
Err error
Value Value
}
// Vector returns a Vector if the result value is one. An error is returned if
// the result was an error or the result value is not a Vector.
func (r *Result) Vector() (Vector, error) {
if r.Err != nil {
return nil, r.Err
}
v, ok := r.Value.(Vector)
if !ok {
return nil, fmt.Errorf("query result is not a Vector")
}
return v, nil
}
// Matrix returns a Matrix. An error is returned if
// the result was an error or the result value is not a Matrix.
func (r *Result) Matrix() (Matrix, error) {
if r.Err != nil {
return nil, r.Err
}
v, ok := r.Value.(Matrix)
if !ok {
return nil, fmt.Errorf("query result is not a range Vector")
}
return v, nil
}
// Scalar returns a Scalar value. An error is returned if
// the result was an error or the result value is not a Scalar.
func (r *Result) Scalar() (Scalar, error) {
if r.Err != nil {
return Scalar{}, r.Err
}
v, ok := r.Value.(Scalar)
if !ok {
return Scalar{}, fmt.Errorf("query result is not a Scalar")
}
return v, nil
}
func (r *Result) String() string {
if r.Err != nil {
return r.Err.Error()
}
if r.Value == nil {
return ""
}
return r.Value.String()
}
type ( type (
// ErrQueryTimeout is returned if a query timed out during processing. // ErrQueryTimeout is returned if a query timed out during processing.
ErrQueryTimeout string ErrQueryTimeout string
@ -531,11 +375,17 @@ func (ng *Engine) populateIterators(ctx context.Context, s *EvalStmt) (storage.Q
Inspect(s.Expr, func(node Node) bool { Inspect(s.Expr, func(node Node) bool {
switch n := node.(type) { switch n := node.(type) {
case *VectorSelector: case *VectorSelector:
if n.Offset > maxOffset { if maxOffset < StalenessDelta {
maxOffset = StalenessDelta
}
if n.Offset+StalenessDelta > maxOffset {
maxOffset = n.Offset + StalenessDelta maxOffset = n.Offset + StalenessDelta
} }
case *MatrixSelector: case *MatrixSelector:
if n.Offset > maxOffset { if maxOffset < n.Range {
maxOffset = n.Range
}
if n.Offset+n.Range > maxOffset {
maxOffset = n.Offset + n.Range maxOffset = n.Offset + n.Range
} }
} }
@ -544,7 +394,7 @@ func (ng *Engine) populateIterators(ctx context.Context, s *EvalStmt) (storage.Q
mint := s.Start.Add(-maxOffset) mint := s.Start.Add(-maxOffset)
querier, err := ng.queryable.Querier(timeMilliseconds(mint), timeMilliseconds(s.End)) querier, err := ng.queryable.Querier(timestamp.FromTime(mint), timestamp.FromTime(s.End))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -554,6 +404,8 @@ func (ng *Engine) populateIterators(ctx context.Context, s *EvalStmt) (storage.Q
case *VectorSelector: case *VectorSelector:
n.series, err = expandSeriesSet(querier.Select(n.LabelMatchers...)) n.series, err = expandSeriesSet(querier.Select(n.LabelMatchers...))
if err != nil { if err != nil {
// TODO(fabxc): use multi-error.
log.Errorln("expand series set:", err)
return false return false
} }
for _, s := range n.series { for _, s := range n.series {
@ -564,6 +416,7 @@ func (ng *Engine) populateIterators(ctx context.Context, s *EvalStmt) (storage.Q
case *MatrixSelector: case *MatrixSelector:
n.series, err = expandSeriesSet(querier.Select(n.LabelMatchers...)) n.series, err = expandSeriesSet(querier.Select(n.LabelMatchers...))
if err != nil { if err != nil {
log.Errorln("expand series set:", err)
return false return false
} }
for _, s := range n.series { for _, s := range n.series {
@ -736,7 +589,7 @@ func (ev *evaluator) eval(expr Expr) Value {
return e.Func.Call(ev, e.Args) return e.Func.Call(ev, e.Args)
case *MatrixSelector: case *MatrixSelector:
return ev.MatrixSelector(e) return ev.matrixSelector(e)
case *NumberLiteral: case *NumberLiteral:
return Scalar{V: e.Val, T: ev.Timestamp} return Scalar{V: e.Val, T: ev.Timestamp}
@ -763,35 +616,33 @@ func (ev *evaluator) eval(expr Expr) Value {
return se return se
case *VectorSelector: case *VectorSelector:
return ev.VectorSelector(e) return ev.vectorSelector(e)
} }
panic(fmt.Errorf("unhandled expression of type: %T", expr)) panic(fmt.Errorf("unhandled expression of type: %T", expr))
} }
// VectorSelector evaluates a *VectorSelector expression. // vectorSelector evaluates a *VectorSelector expression.
func (ev *evaluator) VectorSelector(node *VectorSelector) Vector { func (ev *evaluator) vectorSelector(node *VectorSelector) Vector {
var ( var (
ok bool
vec = make(Vector, 0, len(node.series)) vec = make(Vector, 0, len(node.series))
refTime = ev.Timestamp - durationMilliseconds(node.Offset) refTime = ev.Timestamp - durationMilliseconds(node.Offset)
) )
for i, it := range node.iterators { for i, it := range node.iterators {
if !it.Seek(refTime) { ok := it.Seek(refTime)
if !ok {
if it.Err() != nil { if it.Err() != nil {
ev.error(it.Err()) ev.error(it.Err())
} }
continue
} }
t, v := it.Values() t, v := it.Values()
if t > refTime { if !ok || t > refTime {
t, v, ok = it.PeekBack() t, v, ok = it.PeekBack()
if !ok || t < refTime-durationMilliseconds(StalenessDelta) { if !ok || t < refTime-durationMilliseconds(StalenessDelta) {
continue continue
} }
} }
vec = append(vec, Sample{ vec = append(vec, Sample{
Metric: node.series[i].Labels(), Metric: node.series[i].Labels(),
Point: Point{V: v, T: ev.Timestamp}, Point: Point{V: v, T: ev.Timestamp},
@ -800,8 +651,8 @@ func (ev *evaluator) VectorSelector(node *VectorSelector) Vector {
return vec return vec
} }
// MatrixSelector evaluates a *MatrixSelector expression. // matrixSelector evaluates a *MatrixSelector expression.
func (ev *evaluator) MatrixSelector(node *MatrixSelector) Matrix { func (ev *evaluator) matrixSelector(node *MatrixSelector) Matrix {
var ( var (
offset = durationMilliseconds(node.Offset) offset = durationMilliseconds(node.Offset)
maxt = ev.Timestamp - offset maxt = ev.Timestamp - offset
@ -815,28 +666,30 @@ func (ev *evaluator) MatrixSelector(node *MatrixSelector) Matrix {
Points: make([]Point, 0, 16), Points: make([]Point, 0, 16),
} }
if !it.Seek(maxt) { ok := it.Seek(maxt)
if !ok {
if it.Err() != nil { if it.Err() != nil {
ev.error(it.Err()) ev.error(it.Err())
} }
continue
} }
t, v := it.Values()
buf := it.Buffer() buf := it.Buffer()
for buf.Next() { for buf.Next() {
t, v := buf.Values() t, v := buf.Values()
// Values in the buffer are guaranteed to be smaller than maxt. // Values in the buffer are guaranteed to be smaller than maxt.
if t >= mint { if t >= mint {
ss.Points = append(ss.Points, Point{T: t + offset, V: v}) ss.Points = append(ss.Points, Point{T: t, V: v})
} }
} }
// The seeked sample might also be in the range. // The seeked sample might also be in the range.
t, v := it.Values() t, v = it.Values()
if t == maxt { if t == maxt {
ss.Points = append(ss.Points, Point{T: t + offset, V: v}) ss.Points = append(ss.Points, Point{T: t, V: v})
}
if len(ss.Points) > 0 {
Matrix = append(Matrix, ss)
} }
Matrix = append(Matrix, ss)
} }
return Matrix return Matrix
} }
@ -999,7 +852,7 @@ func (ev *evaluator) VectorBinop(op itemType, lhs, rhs Vector, matching *VectorM
} }
func hashWithoutLabels(lset labels.Labels, names ...string) uint64 { func hashWithoutLabels(lset labels.Labels, names ...string) uint64 {
cm := make(labels.Labels, 0, len(lset)-len(names)-1) cm := make(labels.Labels, 0, len(lset))
Outer: Outer:
for _, l := range lset { for _, l := range lset {
@ -1226,26 +1079,36 @@ func (ev *evaluator) aggregation(op itemType, grouping []string, without bool, k
for _, s := range vec { for _, s := range vec {
lb := labels.NewBuilder(s.Metric) lb := labels.NewBuilder(s.Metric)
if without || keepCommon { if without {
lb.Del(grouping...)
lb.Del(labels.MetricName) lb.Del(labels.MetricName)
} }
if op == itemCountValues { if op == itemCountValues {
lb.Set(valueLabel, fmt.Sprintf("%f", s.V)) // TODO(fabxc): use correct printing. lb.Set(valueLabel, strconv.FormatFloat(float64(s.V), 'f', -1, 64))
} }
var ( var (
groupingKey uint64
metric = lb.Labels() metric = lb.Labels()
groupingKey = metric.Hash()
) )
if without {
groupingKey = metric.Hash()
} else {
groupingKey = hashForLabels(metric, grouping...)
}
group, ok := result[groupingKey] group, ok := result[groupingKey]
// Add a new group if it doesn't exist. // Add a new group if it doesn't exist.
if !ok { if !ok {
var m labels.Labels var m labels.Labels
if keepCommon || without {
if keepCommon {
m = lb.Del(labels.MetricName).Labels()
} else if without {
m = metric m = metric
} else { } else {
m = make(labels.Labels, 0, len(grouping)) m = make(labels.Labels, 0, len(grouping))
for _, l := range s.Metric { for _, l := range metric {
for _, n := range grouping { for _, n := range grouping {
if l.Name == n { if l.Name == n {
m = append(m, labels.Label{Name: n, Value: l.Value}) m = append(m, labels.Label{Name: n, Value: l.Value})
@ -1253,6 +1116,7 @@ func (ev *evaluator) aggregation(op itemType, grouping []string, without bool, k
} }
} }
} }
sort.Sort(m)
} }
result[groupingKey] = &groupedAggregation{ result[groupingKey] = &groupedAggregation{
labels: m, labels: m,
@ -1451,10 +1315,10 @@ func (g *queryGate) Done() {
// user facing terminology as defined in the documentation. // user facing terminology as defined in the documentation.
func documentedType(t ValueType) string { func documentedType(t ValueType) string {
switch t { switch t {
case "Vector": case "vector":
return "instant Vector" return "instant vector"
case "Matrix": case "matrix":
return "range Vector" return "range vector"
default: default:
return string(t) return string(t)
} }

View file

@ -49,13 +49,14 @@ func funcTime(ev *evaluator, args Expressions) Value {
func extrapolatedRate(ev *evaluator, arg Expr, isCounter bool, isRate bool) Value { func extrapolatedRate(ev *evaluator, arg Expr, isCounter bool, isRate bool) Value {
ms := arg.(*MatrixSelector) ms := arg.(*MatrixSelector)
rangeStart := ev.Timestamp - durationMilliseconds(ms.Range+ms.Offset) var (
rangeEnd := ev.Timestamp - durationMilliseconds(ms.Offset) matrix = ev.evalMatrix(ms)
rangeStart = ev.Timestamp - durationMilliseconds(ms.Range+ms.Offset)
rangeEnd = ev.Timestamp - durationMilliseconds(ms.Offset)
resultVector = make(Vector, 0, len(matrix))
)
resultVector := Vector{} for _, samples := range matrix {
MatrixValue := ev.evalMatrix(ms)
for _, samples := range MatrixValue {
// No sense in trying to compute a rate without at least two points. Drop // No sense in trying to compute a rate without at least two points. Drop
// this Vector element. // this Vector element.
if len(samples.Points) < 2 { if len(samples.Points) < 2 {
@ -74,11 +75,11 @@ func extrapolatedRate(ev *evaluator, arg Expr, isCounter bool, isRate bool) Valu
resultValue := lastValue - samples.Points[0].V + counterCorrection resultValue := lastValue - samples.Points[0].V + counterCorrection
// Duration between first/last samples and boundary of range. // Duration between first/last samples and boundary of range.
durationToStart := float64(samples.Points[0].T - rangeStart) durationToStart := float64(samples.Points[0].T-rangeStart) / 1000
durationToEnd := float64(rangeEnd - samples.Points[len(samples.Points)-1].T) durationToEnd := float64(rangeEnd-samples.Points[len(samples.Points)-1].T) / 1000
sampledInterval := float64(samples.Points[len(samples.Points)-1].T - samples.Points[0].T) sampledInterval := float64(samples.Points[len(samples.Points)-1].T-samples.Points[0].T) / 1000
averageDurationBetweenSamples := float64(sampledInterval) / float64(len(samples.Points)-1) averageDurationBetweenSamples := sampledInterval / float64(len(samples.Points)-1)
if isCounter && resultValue > 0 && samples.Points[0].V >= 0 { if isCounter && resultValue > 0 && samples.Points[0].V >= 0 {
// Counters cannot be negative. If we have any slope at // Counters cannot be negative. If we have any slope at
@ -88,7 +89,7 @@ func extrapolatedRate(ev *evaluator, arg Expr, isCounter bool, isRate bool) Valu
// take the zero point as the start of the series, // take the zero point as the start of the series,
// thereby avoiding extrapolation to negative counter // thereby avoiding extrapolation to negative counter
// values. // values.
durationToZero := float64(sampledInterval) * float64(samples.Points[0].V/resultValue) durationToZero := sampledInterval * (samples.Points[0].V / resultValue)
if durationToZero < durationToStart { if durationToZero < durationToStart {
durationToStart = durationToZero durationToStart = durationToZero
} }
@ -111,9 +112,9 @@ func extrapolatedRate(ev *evaluator, arg Expr, isCounter bool, isRate bool) Valu
} else { } else {
extrapolateToInterval += averageDurationBetweenSamples / 2 extrapolateToInterval += averageDurationBetweenSamples / 2
} }
resultValue = resultValue * extrapolateToInterval / sampledInterval resultValue = resultValue * (extrapolateToInterval / sampledInterval)
if isRate { if isRate {
resultValue = resultValue / 1000 / ms.Range.Seconds() resultValue = resultValue / ms.Range.Seconds()
} }
resultVector = append(resultVector, Sample{ resultVector = append(resultVector, Sample{
@ -303,7 +304,9 @@ func funcSortDesc(ev *evaluator, args Expressions) Value {
func funcClampMax(ev *evaluator, args Expressions) Value { func funcClampMax(ev *evaluator, args Expressions) Value {
vec := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
max := ev.evalFloat(args[1]) max := ev.evalFloat(args[1])
for _, el := range vec { for i := range vec {
el := &vec[i]
el.Metric = dropMetricName(el.Metric) el.Metric = dropMetricName(el.Metric)
el.V = math.Min(max, float64(el.V)) el.V = math.Min(max, float64(el.V))
} }
@ -314,7 +317,9 @@ func funcClampMax(ev *evaluator, args Expressions) Value {
func funcClampMin(ev *evaluator, args Expressions) Value { func funcClampMin(ev *evaluator, args Expressions) Value {
vec := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
min := ev.evalFloat(args[1]) min := ev.evalFloat(args[1])
for _, el := range vec { for i := range vec {
el := &vec[i]
el.Metric = dropMetricName(el.Metric) el.Metric = dropMetricName(el.Metric)
el.V = math.Max(min, float64(el.V)) el.V = math.Max(min, float64(el.V))
} }
@ -356,7 +361,8 @@ func funcDropCommonLabels(ev *evaluator, args Expressions) Value {
cnames = append(cnames, n) cnames = append(cnames, n)
} }
for _, el := range vec { for i := range vec {
el := &vec[i]
el.Metric = labels.NewBuilder(el.Metric).Del(cnames...).Labels() el.Metric = labels.NewBuilder(el.Metric).Del(cnames...).Labels()
} }
return vec return vec
@ -374,7 +380,9 @@ func funcRound(ev *evaluator, args Expressions) Value {
toNearestInverse := 1.0 / toNearest toNearestInverse := 1.0 / toNearest
vec := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
for _, el := range vec { for i := range vec {
el := &vec[i]
el.Metric = dropMetricName(el.Metric) el.Metric = dropMetricName(el.Metric)
el.V = math.Floor(float64(el.V)*toNearestInverse+0.5) / toNearestInverse el.V = math.Floor(float64(el.V)*toNearestInverse+0.5) / toNearestInverse
} }
@ -396,7 +404,7 @@ func funcScalar(ev *evaluator, args Expressions) Value {
} }
} }
// === count_Scalar(Vector ValueTypeVector) float64 === // === count_scalar(Vector ValueTypeVector) float64 ===
func funcCountScalar(ev *evaluator, args Expressions) Value { func funcCountScalar(ev *evaluator, args Expressions) Value {
return Scalar{ return Scalar{
V: float64(len(ev.evalVector(args[0]))), V: float64(len(ev.evalVector(args[0]))),
@ -441,12 +449,14 @@ func funcCountOverTime(ev *evaluator, args Expressions) Value {
// === floor(Vector ValueTypeVector) Vector === // === floor(Vector ValueTypeVector) Vector ===
func funcFloor(ev *evaluator, args Expressions) Value { func funcFloor(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
for _, el := range Vector { for i := range vec {
el := &vec[i]
el.Metric = dropMetricName(el.Metric) el.Metric = dropMetricName(el.Metric)
el.V = math.Floor(float64(el.V)) el.V = math.Floor(float64(el.V))
} }
return Vector return vec
} }
// === max_over_time(Matrix ValueTypeMatrix) Vector === // === max_over_time(Matrix ValueTypeMatrix) Vector ===
@ -536,12 +546,14 @@ func funcStdvarOverTime(ev *evaluator, args Expressions) Value {
// === abs(Vector ValueTypeVector) Vector === // === abs(Vector ValueTypeVector) Vector ===
func funcAbs(ev *evaluator, args Expressions) Value { func funcAbs(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
for _, el := range Vector { for i := range vec {
el := &vec[i]
el.Metric = dropMetricName(el.Metric) el.Metric = dropMetricName(el.Metric)
el.V = math.Abs(float64(el.V)) el.V = math.Abs(float64(el.V))
} }
return Vector return vec
} }
// === absent(Vector ValueTypeVector) Vector === // === absent(Vector ValueTypeVector) Vector ===
@ -568,62 +580,74 @@ func funcAbsent(ev *evaluator, args Expressions) Value {
// === ceil(Vector ValueTypeVector) Vector === // === ceil(Vector ValueTypeVector) Vector ===
func funcCeil(ev *evaluator, args Expressions) Value { func funcCeil(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
for _, el := range Vector { for i := range vec {
el := &vec[i]
el.Metric = dropMetricName(el.Metric) el.Metric = dropMetricName(el.Metric)
el.V = math.Ceil(float64(el.V)) el.V = math.Ceil(float64(el.V))
} }
return Vector return vec
} }
// === exp(Vector ValueTypeVector) Vector === // === exp(Vector ValueTypeVector) Vector ===
func funcExp(ev *evaluator, args Expressions) Value { func funcExp(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
for _, el := range Vector { for i := range vec {
el := &vec[i]
el.Metric = dropMetricName(el.Metric) el.Metric = dropMetricName(el.Metric)
el.V = math.Exp(float64(el.V)) el.V = math.Exp(float64(el.V))
} }
return Vector return vec
} }
// === sqrt(Vector VectorNode) Vector === // === sqrt(Vector VectorNode) Vector ===
func funcSqrt(ev *evaluator, args Expressions) Value { func funcSqrt(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
for _, el := range Vector { for i := range vec {
el := &vec[i]
el.Metric = dropMetricName(el.Metric) el.Metric = dropMetricName(el.Metric)
el.V = math.Sqrt(float64(el.V)) el.V = math.Sqrt(float64(el.V))
} }
return Vector return vec
} }
// === ln(Vector ValueTypeVector) Vector === // === ln(Vector ValueTypeVector) Vector ===
func funcLn(ev *evaluator, args Expressions) Value { func funcLn(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
for _, el := range Vector { for i := range vec {
el := &vec[i]
el.Metric = dropMetricName(el.Metric) el.Metric = dropMetricName(el.Metric)
el.V = math.Log(float64(el.V)) el.V = math.Log(float64(el.V))
} }
return Vector return vec
} }
// === log2(Vector ValueTypeVector) Vector === // === log2(Vector ValueTypeVector) Vector ===
func funcLog2(ev *evaluator, args Expressions) Value { func funcLog2(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
for _, el := range Vector { for i := range vec {
el := &vec[i]
el.Metric = dropMetricName(el.Metric) el.Metric = dropMetricName(el.Metric)
el.V = math.Log2(float64(el.V)) el.V = math.Log2(float64(el.V))
} }
return Vector return vec
} }
// === log10(Vector ValueTypeVector) Vector === // === log10(Vector ValueTypeVector) Vector ===
func funcLog10(ev *evaluator, args Expressions) Value { func funcLog10(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
for _, el := range Vector { for i := range vec {
el := &vec[i]
el.Metric = dropMetricName(el.Metric) el.Metric = dropMetricName(el.Metric)
el.V = math.Log10(float64(el.V)) el.V = math.Log10(float64(el.V))
} }
return Vector return vec
} }
// linearRegression performs a least-square linear regression analysis on the // linearRegression performs a least-square linear regression analysis on the
@ -636,7 +660,7 @@ func linearRegression(samples []Point, interceptTime int64) (slope, intercept fl
sumXY, sumX2 float64 sumXY, sumX2 float64
) )
for _, sample := range samples { for _, sample := range samples {
x := float64(sample.T-interceptTime) / 1e6 x := float64(sample.T-interceptTime) / 1e3
n += 1.0 n += 1.0
sumY += sample.V sumY += sample.V
sumX += x sumX += x
@ -786,7 +810,7 @@ func funcChanges(ev *evaluator, args Expressions) Value {
// === label_replace(Vector ValueTypeVector, dst_label, replacement, src_labelname, regex ValueTypeString) Vector === // === label_replace(Vector ValueTypeVector, dst_label, replacement, src_labelname, regex ValueTypeString) Vector ===
func funcLabelReplace(ev *evaluator, args Expressions) Value { func funcLabelReplace(ev *evaluator, args Expressions) Value {
var ( var (
Vector = ev.evalVector(args[0]) vector = ev.evalVector(args[0])
dst = ev.evalString(args[1]).V dst = ev.evalString(args[1]).V
repl = ev.evalString(args[2]).V repl = ev.evalString(args[2]).V
src = ev.evalString(args[3]).V src = ev.evalString(args[3]).V
@ -801,8 +825,10 @@ func funcLabelReplace(ev *evaluator, args Expressions) Value {
ev.errorf("invalid destination label name in label_replace(): %s", dst) ev.errorf("invalid destination label name in label_replace(): %s", dst)
} }
outSet := make(map[uint64]struct{}, len(Vector)) outSet := make(map[uint64]struct{}, len(vector))
for _, el := range Vector { for i := range vector {
el := &vector[i]
srcVal := el.Metric.Get(src) srcVal := el.Metric.Get(src)
indexes := regex.FindStringSubmatchIndex(srcVal) indexes := regex.FindStringSubmatchIndex(srcVal)
// If there is no match, no replacement should take place. // If there is no match, no replacement should take place.
@ -825,7 +851,7 @@ func funcLabelReplace(ev *evaluator, args Expressions) Value {
} }
} }
return Vector return vector
} }
// === Vector(s Scalar) Vector === // === Vector(s Scalar) Vector ===
@ -851,7 +877,9 @@ func dateWrapper(ev *evaluator, args Expressions, f func(time.Time) float64) Val
} else { } else {
v = ev.evalVector(args[0]) v = ev.evalVector(args[0])
} }
for _, el := range v { for i := range v {
el := &v[i]
el.Metric = dropMetricName(el.Metric) el.Metric = dropMetricName(el.Metric)
t := time.Unix(int64(el.V), 0).UTC() t := time.Unix(int64(el.V), 0).UTC()
el.V = f(t) el.V = f(t)
@ -957,8 +985,8 @@ var functions = map[string]*Function{
ReturnType: ValueTypeVector, ReturnType: ValueTypeVector,
Call: funcCountOverTime, Call: funcCountOverTime,
}, },
"count_Scalar": { "count_scalar": {
Name: "count_Scalar", Name: "count_scalar",
ArgTypes: []ValueType{ValueTypeVector}, ArgTypes: []ValueType{ValueTypeVector},
ReturnType: ValueTypeScalar, ReturnType: ValueTypeScalar,
Call: funcCountScalar, Call: funcCountScalar,
@ -1132,8 +1160,8 @@ var functions = map[string]*Function{
ReturnType: ValueTypeVector, ReturnType: ValueTypeVector,
Call: funcRound, Call: funcRound,
}, },
"Scalar": { "scalar": {
Name: "Scalar", Name: "scalar",
ArgTypes: []ValueType{ValueTypeVector}, ArgTypes: []ValueType{ValueTypeVector},
ReturnType: ValueTypeScalar, ReturnType: ValueTypeScalar,
Call: funcScalar, Call: funcScalar,
@ -1180,8 +1208,8 @@ var functions = map[string]*Function{
ReturnType: ValueTypeScalar, ReturnType: ValueTypeScalar,
Call: funcTime, Call: funcTime,
}, },
"Vector": { "vector": {
Name: "Vector", Name: "vector",
ArgTypes: []ValueType{ValueTypeScalar}, ArgTypes: []ValueType{ValueTypeScalar},
ReturnType: ValueTypeVector, ReturnType: ValueTypeVector,
Call: funcVector, Call: funcVector,
@ -1219,7 +1247,7 @@ func (s vectorByValueHeap) Swap(i, j int) {
} }
func (s *vectorByValueHeap) Push(x interface{}) { func (s *vectorByValueHeap) Push(x interface{}) {
*s = append(*s, x.(Sample)) *s = append(*s, *(x.(*Sample)))
} }
func (s *vectorByValueHeap) Pop() interface{} { func (s *vectorByValueHeap) Pop() interface{} {
@ -1248,7 +1276,7 @@ func (s vectorByReverseValueHeap) Swap(i, j int) {
} }
func (s *vectorByReverseValueHeap) Push(x interface{}) { func (s *vectorByReverseValueHeap) Push(x interface{}) {
*s = append(*s, x.(Sample)) *s = append(*s, *(x.(*Sample)))
} }
func (s *vectorByReverseValueHeap) Pop() interface{} { func (s *vectorByReverseValueHeap) Pop() interface{} {

View file

@ -506,7 +506,7 @@ func (p *parser) balance(lhs Expr, op itemType, rhs Expr, vecMatching *VectorMat
if (precd < 0) || (precd == 0 && op.isRightAssociative()) { if (precd < 0) || (precd == 0 && op.isRightAssociative()) {
balanced := p.balance(lhsBE.RHS, op, rhs, vecMatching, returnBool) balanced := p.balance(lhsBE.RHS, op, rhs, vecMatching, returnBool)
if lhsBE.Op.isComparisonOperator() && !lhsBE.ReturnBool && balanced.Type() == ValueTypeScalar && lhsBE.LHS.Type() == ValueTypeScalar { if lhsBE.Op.isComparisonOperator() && !lhsBE.ReturnBool && balanced.Type() == ValueTypeScalar && lhsBE.LHS.Type() == ValueTypeScalar {
p.errorf("comparisons between Scalars must use BOOL modifier") p.errorf("comparisons between scalars must use BOOL modifier")
} }
return &BinaryExpr{ return &BinaryExpr{
Op: lhsBE.Op, Op: lhsBE.Op,
@ -518,7 +518,7 @@ func (p *parser) balance(lhs Expr, op itemType, rhs Expr, vecMatching *VectorMat
} }
} }
if op.isComparisonOperator() && !returnBool && rhs.Type() == ValueTypeScalar && lhs.Type() == ValueTypeScalar { if op.isComparisonOperator() && !returnBool && rhs.Type() == ValueTypeScalar && lhs.Type() == ValueTypeScalar {
p.errorf("comparisons between Scalars must use BOOL modifier") p.errorf("comparisons between scalars must use BOOL modifier")
} }
return &BinaryExpr{ return &BinaryExpr{
Op: op, Op: op,
@ -935,7 +935,7 @@ func (p *parser) offset() time.Duration {
return offset return offset
} }
// VectorSelector parses a new (instant) Vector selector. // VectorSelector parses a new (instant) vector selector.
// //
// <metric_identifier> [<label_matchers>] // <metric_identifier> [<label_matchers>]
// [<metric_identifier>] <label_matchers> // [<metric_identifier>] <label_matchers>
@ -962,7 +962,7 @@ func (p *parser) VectorSelector(name string) *VectorSelector {
} }
if len(matchers) == 0 { if len(matchers) == 0 {
p.errorf("Vector selector must contain label matchers or metric name") p.errorf("vector selector must contain label matchers or metric name")
} }
// A Vector selector must contain at least one non-empty matcher to prevent // A Vector selector must contain at least one non-empty matcher to prevent
// implicit selection of all metrics (e.g. by a typo). // implicit selection of all metrics (e.g. by a typo).
@ -974,7 +974,7 @@ func (p *parser) VectorSelector(name string) *VectorSelector {
} }
} }
if !notEmpty { if !notEmpty {
p.errorf("Vector selector must contain at least one non-empty matcher") p.errorf("vector selector must contain at least one non-empty matcher")
} }
return &VectorSelector{ return &VectorSelector{
@ -1028,7 +1028,7 @@ func (p *parser) checkType(node Node) (typ ValueType) {
case *RecordStmt: case *RecordStmt:
ty := p.checkType(n.Expr) ty := p.checkType(n.Expr)
if ty != ValueTypeVector && ty != ValueTypeScalar { if ty != ValueTypeVector && ty != ValueTypeScalar {
p.errorf("record statement must have a valid expression of type instant Vector or Scalar but got %s", documentedType(ty)) p.errorf("record statement must have a valid expression of type instant vector or scalar but got %s", documentedType(ty))
} }
case Expressions: case Expressions:
@ -1058,12 +1058,12 @@ func (p *parser) checkType(node Node) (typ ValueType) {
p.errorf("binary expression does not support operator %q", n.Op) p.errorf("binary expression does not support operator %q", n.Op)
} }
if (lt != ValueTypeScalar && lt != ValueTypeVector) || (rt != ValueTypeScalar && rt != ValueTypeVector) { if (lt != ValueTypeScalar && lt != ValueTypeVector) || (rt != ValueTypeScalar && rt != ValueTypeVector) {
p.errorf("binary expression must contain only Scalar and instant Vector types") p.errorf("binary expression must contain only scalar and instant vector types")
} }
if (lt != ValueTypeVector || rt != ValueTypeVector) && n.VectorMatching != nil { if (lt != ValueTypeVector || rt != ValueTypeVector) && n.VectorMatching != nil {
if len(n.VectorMatching.MatchingLabels) > 0 { if len(n.VectorMatching.MatchingLabels) > 0 {
p.errorf("Vector matching only allowed between instant Vectors") p.errorf("vector matching only allowed between instant vectors")
} }
n.VectorMatching = nil n.VectorMatching = nil
} else { } else {
@ -1079,7 +1079,7 @@ func (p *parser) checkType(node Node) (typ ValueType) {
} }
if (lt == ValueTypeScalar || rt == ValueTypeScalar) && n.Op.isSetOperator() { if (lt == ValueTypeScalar || rt == ValueTypeScalar) && n.Op.isSetOperator() {
p.errorf("set operator %q not allowed in binary Scalar expression", n.Op) p.errorf("set operator %q not allowed in binary scalar expression", n.Op)
} }
case *Call: case *Call:
@ -1102,7 +1102,7 @@ func (p *parser) checkType(node Node) (typ ValueType) {
p.errorf("only + and - operators allowed for unary expressions") p.errorf("only + and - operators allowed for unary expressions")
} }
if t := p.checkType(n.Expr); t != ValueTypeScalar && t != ValueTypeVector { if t := p.checkType(n.Expr); t != ValueTypeScalar && t != ValueTypeVector {
p.errorf("unary expression only allowed on expressions of type Scalar or instant Vector, got %q", documentedType(t)) p.errorf("unary expression only allowed on expressions of type scalar or instant vector, got %q", documentedType(t))
} }
case *NumberLiteral, *MatrixSelector, *StringLiteral, *VectorSelector: case *NumberLiteral, *MatrixSelector, *StringLiteral, *VectorSelector:

View file

@ -23,6 +23,7 @@ import (
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
"github.com/prometheus/prometheus/pkg/labels" "github.com/prometheus/prometheus/pkg/labels"
"github.com/stretchr/testify/require"
) )
var testExpr = []struct { var testExpr = []struct {
@ -31,7 +32,7 @@ var testExpr = []struct {
fail bool // Whether parsing is supposed to fail. fail bool // Whether parsing is supposed to fail.
errMsg string // If not empty the parsing error has to contain this string. errMsg string // If not empty the parsing error has to contain this string.
}{ }{
// Scalars and Scalar-to-Scalar operations. // Scalars and scalar-to-scalar operations.
{ {
input: "1", input: "1",
expected: &NumberLiteral{1}, expected: &NumberLiteral{1},
@ -212,19 +213,19 @@ var testExpr = []struct {
}, { }, {
input: "1 and 1", input: "1 and 1",
fail: true, fail: true,
errMsg: "set operator \"and\" not allowed in binary Scalar expression", errMsg: "set operator \"and\" not allowed in binary scalar expression",
}, { }, {
input: "1 == 1", input: "1 == 1",
fail: true, fail: true,
errMsg: "parse error at char 7: comparisons between Scalars must use BOOL modifier", errMsg: "parse error at char 7: comparisons between scalars must use BOOL modifier",
}, { }, {
input: "1 or 1", input: "1 or 1",
fail: true, fail: true,
errMsg: "set operator \"or\" not allowed in binary Scalar expression", errMsg: "set operator \"or\" not allowed in binary scalar expression",
}, { }, {
input: "1 unless 1", input: "1 unless 1",
fail: true, fail: true,
errMsg: "set operator \"unless\" not allowed in binary Scalar expression", errMsg: "set operator \"unless\" not allowed in binary scalar expression",
}, { }, {
input: "1 !~ 1", input: "1 !~ 1",
fail: true, fail: true,
@ -236,11 +237,11 @@ var testExpr = []struct {
}, { }, {
input: `-"string"`, input: `-"string"`,
fail: true, fail: true,
errMsg: `unary expression only allowed on expressions of type Scalar or instant Vector, got "string"`, errMsg: `unary expression only allowed on expressions of type scalar or instant vector, got "string"`,
}, { }, {
input: `-test[5m]`, input: `-test[5m]`,
fail: true, fail: true,
errMsg: `unary expression only allowed on expressions of type Scalar or instant Vector, got "range Vector"`, errMsg: `unary expression only allowed on expressions of type scalar or instant vector, got "range vector"`,
}, { }, {
input: `*test`, input: `*test`,
fail: true, fail: true,
@ -747,35 +748,35 @@ var testExpr = []struct {
}, { }, {
input: "foo and 1", input: "foo and 1",
fail: true, fail: true,
errMsg: "set operator \"and\" not allowed in binary Scalar expression", errMsg: "set operator \"and\" not allowed in binary scalar expression",
}, { }, {
input: "1 and foo", input: "1 and foo",
fail: true, fail: true,
errMsg: "set operator \"and\" not allowed in binary Scalar expression", errMsg: "set operator \"and\" not allowed in binary scalar expression",
}, { }, {
input: "foo or 1", input: "foo or 1",
fail: true, fail: true,
errMsg: "set operator \"or\" not allowed in binary Scalar expression", errMsg: "set operator \"or\" not allowed in binary scalar expression",
}, { }, {
input: "1 or foo", input: "1 or foo",
fail: true, fail: true,
errMsg: "set operator \"or\" not allowed in binary Scalar expression", errMsg: "set operator \"or\" not allowed in binary scalar expression",
}, { }, {
input: "foo unless 1", input: "foo unless 1",
fail: true, fail: true,
errMsg: "set operator \"unless\" not allowed in binary Scalar expression", errMsg: "set operator \"unless\" not allowed in binary scalar expression",
}, { }, {
input: "1 unless foo", input: "1 unless foo",
fail: true, fail: true,
errMsg: "set operator \"unless\" not allowed in binary Scalar expression", errMsg: "set operator \"unless\" not allowed in binary scalar expression",
}, { }, {
input: "1 or on(bar) foo", input: "1 or on(bar) foo",
fail: true, fail: true,
errMsg: "Vector matching only allowed between instant Vectors", errMsg: "vector matching only allowed between instant vectors",
}, { }, {
input: "foo == on(bar) 10", input: "foo == on(bar) 10",
fail: true, fail: true,
errMsg: "Vector matching only allowed between instant Vectors", errMsg: "vector matching only allowed between instant vectors",
}, { }, {
input: "foo and on(bar) group_left(baz) bar", input: "foo and on(bar) group_left(baz) bar",
fail: true, fail: true,
@ -914,23 +915,23 @@ var testExpr = []struct {
}, { }, {
input: `{}`, input: `{}`,
fail: true, fail: true,
errMsg: "Vector selector must contain label matchers or metric name", errMsg: "vector selector must contain label matchers or metric name",
}, { }, {
input: `{x=""}`, input: `{x=""}`,
fail: true, fail: true,
errMsg: "Vector selector must contain at least one non-empty matcher", errMsg: "vector selector must contain at least one non-empty matcher",
}, { }, {
input: `{x=~".*"}`, input: `{x=~".*"}`,
fail: true, fail: true,
errMsg: "Vector selector must contain at least one non-empty matcher", errMsg: "vector selector must contain at least one non-empty matcher",
}, { }, {
input: `{x!~".+"}`, input: `{x!~".+"}`,
fail: true, fail: true,
errMsg: "Vector selector must contain at least one non-empty matcher", errMsg: "vector selector must contain at least one non-empty matcher",
}, { }, {
input: `{x!="a"}`, input: `{x!="a"}`,
fail: true, fail: true,
errMsg: "Vector selector must contain at least one non-empty matcher", errMsg: "vector selector must contain at least one non-empty matcher",
}, { }, {
input: `foo{__name__="bar"}`, input: `foo{__name__="bar"}`,
fail: true, fail: true,
@ -1285,11 +1286,11 @@ var testExpr = []struct {
}, { }, {
input: `topk(some_metric, other_metric)`, input: `topk(some_metric, other_metric)`,
fail: true, fail: true,
errMsg: "parse error at char 32: expected type Scalar in aggregation parameter, got instant Vector", errMsg: "parse error at char 32: expected type scalar in aggregation parameter, got instant vector",
}, { }, {
input: `count_values(5, other_metric)`, input: `count_values(5, other_metric)`,
fail: true, fail: true,
errMsg: "parse error at char 30: expected type string in aggregation parameter, got Scalar", errMsg: "parse error at char 30: expected type string in aggregation parameter, got scalar",
}, },
// Test function calls. // Test function calls.
{ {
@ -1363,7 +1364,7 @@ var testExpr = []struct {
}, { }, {
input: "floor(1)", input: "floor(1)",
fail: true, fail: true,
errMsg: "expected type instant Vector in call to function \"floor\", got Scalar", errMsg: "expected type instant vector in call to function \"floor\", got scalar",
}, { }, {
input: "non_existent_function_far_bar()", input: "non_existent_function_far_bar()",
fail: true, fail: true,
@ -1371,7 +1372,7 @@ var testExpr = []struct {
}, { }, {
input: "rate(some_metric)", input: "rate(some_metric)",
fail: true, fail: true,
errMsg: "expected type range Vector in call to function \"rate\", got instant Vector", errMsg: "expected type range vector in call to function \"rate\", got instant vector",
}, },
// Fuzzing regression tests. // Fuzzing regression tests.
{ {
@ -1532,7 +1533,7 @@ var testStatement = []struct {
summary = "Global request rate low", summary = "Global request rate low",
description = "The global request rate is low" description = "The global request rate is low"
} }
foo = bar{label1="value1"} foo = bar{label1="value1"}
ALERT BazAlert IF foo > 10 ALERT BazAlert IF foo > 10
@ -1591,7 +1592,6 @@ var testStatement = []struct {
mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"), mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
}, },
}, },
Labels: nil,
}, },
&AlertStmt{ &AlertStmt{
Name: "BazAlert", Name: "BazAlert",
@ -1605,7 +1605,6 @@ var testStatement = []struct {
}, },
RHS: &NumberLiteral{10}, RHS: &NumberLiteral{10},
}, },
Labels: labels.Labels{},
Annotations: labels.FromStrings( Annotations: labels.FromStrings(
"summary", "Baz", "summary", "Baz",
"description", "BazAlert", "description", "BazAlert",
@ -1782,59 +1781,41 @@ func mustGetFunction(name string) *Function {
var testSeries = []struct { var testSeries = []struct {
input string input string
expectedMetric model.Metric expectedMetric labels.Labels
expectedValues []sequenceValue expectedValues []sequenceValue
fail bool fail bool
}{ }{
{ {
input: `{} 1 2 3`, input: `{} 1 2 3`,
expectedMetric: model.Metric{}, expectedMetric: labels.Labels{},
expectedValues: newSeq(1, 2, 3), expectedValues: newSeq(1, 2, 3),
}, { }, {
input: `{a="b"} -1 2 3`, input: `{a="b"} -1 2 3`,
expectedMetric: model.Metric{ expectedMetric: labels.FromStrings("a", "b"),
"a": "b",
},
expectedValues: newSeq(-1, 2, 3), expectedValues: newSeq(-1, 2, 3),
}, { }, {
input: `my_metric 1 2 3`, input: `my_metric 1 2 3`,
expectedMetric: model.Metric{ expectedMetric: labels.FromStrings(labels.MetricName, "my_metric"),
model.MetricNameLabel: "my_metric",
},
expectedValues: newSeq(1, 2, 3), expectedValues: newSeq(1, 2, 3),
}, { }, {
input: `my_metric{} 1 2 3`, input: `my_metric{} 1 2 3`,
expectedMetric: model.Metric{ expectedMetric: labels.FromStrings(labels.MetricName, "my_metric"),
model.MetricNameLabel: "my_metric",
},
expectedValues: newSeq(1, 2, 3), expectedValues: newSeq(1, 2, 3),
}, { }, {
input: `my_metric{a="b"} 1 2 3`, input: `my_metric{a="b"} 1 2 3`,
expectedMetric: model.Metric{ expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
model.MetricNameLabel: "my_metric",
"a": "b",
},
expectedValues: newSeq(1, 2, 3), expectedValues: newSeq(1, 2, 3),
}, { }, {
input: `my_metric{a="b"} 1 2 3-10x4`, input: `my_metric{a="b"} 1 2 3-10x4`,
expectedMetric: model.Metric{ expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
model.MetricNameLabel: "my_metric",
"a": "b",
},
expectedValues: newSeq(1, 2, 3, -7, -17, -27, -37), expectedValues: newSeq(1, 2, 3, -7, -17, -27, -37),
}, { }, {
input: `my_metric{a="b"} 1 2 3-0x4`, input: `my_metric{a="b"} 1 2 3-0x4`,
expectedMetric: model.Metric{ expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
model.MetricNameLabel: "my_metric",
"a": "b",
},
expectedValues: newSeq(1, 2, 3, 3, 3, 3, 3), expectedValues: newSeq(1, 2, 3, 3, 3, 3, 3),
}, { }, {
input: `my_metric{a="b"} 1 3 _ 5 _x4`, input: `my_metric{a="b"} 1 3 _ 5 _x4`,
expectedMetric: model.Metric{ expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
model.MetricNameLabel: "my_metric",
"a": "b",
},
expectedValues: newSeq(1, 3, none, 5, none, none, none, none), expectedValues: newSeq(1, 3, none, 5, none, none, none, none),
}, { }, {
input: `my_metric{a="b"} 1 3 _ 5 _a4`, input: `my_metric{a="b"} 1 3 _ 5 _a4`,
@ -1884,6 +1865,9 @@ func TestParseSeries(t *testing.T) {
t.Fatalf("failure expected, but passed") t.Fatalf("failure expected, but passed")
} }
require.Equal(t, test.expectedMetric, metric)
require.Equal(t, test.expectedValues, vals)
if !reflect.DeepEqual(vals, test.expectedValues) || !reflect.DeepEqual(metric, test.expectedMetric) { if !reflect.DeepEqual(vals, test.expectedValues) || !reflect.DeepEqual(metric, test.expectedMetric) {
t.Errorf("error in input: \n\n%s\n", test.input) t.Errorf("error in input: \n\n%s\n", test.input)
t.Fatalf("no match\n\nexpected:\n%s %s\ngot: \n%s %s\n", test.expectedMetric, test.expectedValues, metric, vals) t.Fatalf("no match\n\nexpected:\n%s %s\ngot: \n%s %s\n", test.expectedMetric, test.expectedValues, metric, vals)

View file

@ -146,7 +146,7 @@ func (node *AggregateExpr) String() string {
} else { } else {
format = "%s BY (%s)" format = "%s BY (%s)"
} }
aggrString = fmt.Sprintf(format, aggrString, node.Grouping) aggrString = fmt.Sprintf(format, aggrString, strings.Join(node.Grouping, ", "))
} }
if node.KeepCommonLabels { if node.KeepCommonLabels {
aggrString += " KEEP_COMMON" aggrString += " KEEP_COMMON"
@ -164,9 +164,9 @@ func (node *BinaryExpr) String() string {
vm := node.VectorMatching vm := node.VectorMatching
if vm != nil && (len(vm.MatchingLabels) > 0 || vm.On) { if vm != nil && (len(vm.MatchingLabels) > 0 || vm.On) {
if vm.On { if vm.On {
matching = fmt.Sprintf(" ON(%s)", vm.MatchingLabels) matching = fmt.Sprintf(" ON(%s)", strings.Join(vm.MatchingLabels, ", "))
} else { } else {
matching = fmt.Sprintf(" IGNORING(%s)", vm.MatchingLabels) matching = fmt.Sprintf(" IGNORING(%s)", strings.Join(vm.MatchingLabels, ", "))
} }
if vm.Card == CardManyToOne || vm.Card == CardOneToMany { if vm.Card == CardManyToOne || vm.Card == CardOneToMany {
matching += " GROUP_" matching += " GROUP_"
@ -175,7 +175,7 @@ func (node *BinaryExpr) String() string {
} else { } else {
matching += "RIGHT" matching += "RIGHT"
} }
matching += fmt.Sprintf("(%s)", vm.Include) matching += fmt.Sprintf("(%s)", strings.Join(vm.Include, ", "))
} }
} }
return fmt.Sprintf("%s %s%s%s %s", node.LHS, node.Op, returnBool, matching, node.RHS) return fmt.Sprintf("%s %s%s%s %s", node.LHS, node.Op, returnBool, matching, node.RHS)

View file

@ -42,7 +42,7 @@ const (
epsilon = 0.000001 // Relative error allowed for sample values. epsilon = 0.000001 // Relative error allowed for sample values.
) )
var testStartTime = time.Time{} var testStartTime = time.Unix(0, 0)
// Test is a sequence of read and write commands that are run // Test is a sequence of read and write commands that are run
// against a test storage. // against a test storage.
@ -281,18 +281,13 @@ func (cmd *loadCmd) set(m labels.Labels, vals ...sequenceValue) {
// append the defined time series to the storage. // append the defined time series to the storage.
func (cmd *loadCmd) append(a storage.Appender) { func (cmd *loadCmd) append(a storage.Appender) {
// TODO(fabxc): commented out until Appender refactoring. for h, smpls := range cmd.defs {
// for fp, samples := range cmd.defs { m := cmd.metrics[h]
// met := cmd.metrics[fp]
// for _, smpl := range samples { for _, s := range smpls {
// s := &model.Sample{ a.Add(m, s.T, s.V)
// Metric: met, }
// Value: smpl.Value, }
// Timestamp: smpl.Timestamp,
// }
// a.Append(s)
// }
// }
} }
// evalCmd is a command that evaluates an expression for the given time (range) // evalCmd is a command that evaluates an expression for the given time (range)
@ -381,6 +376,7 @@ func (ev *evalCmd) compareResult(result Value) error {
if !ev.instant { if !ev.instant {
return fmt.Errorf("received instant result on range evaluation") return fmt.Errorf("received instant result on range evaluation")
} }
seen := map[uint64]bool{} seen := map[uint64]bool{}
for pos, v := range val { for pos, v := range val {
fp := v.Metric.Hash() fp := v.Metric.Hash()
@ -391,7 +387,7 @@ func (ev *evalCmd) compareResult(result Value) error {
if ev.ordered && exp.pos != pos+1 { if ev.ordered && exp.pos != pos+1 {
return fmt.Errorf("expected metric %s with %v at position %d but was at %d", v.Metric, exp.vals, exp.pos, pos+1) return fmt.Errorf("expected metric %s with %v at position %d but was at %d", v.Metric, exp.vals, exp.pos, pos+1)
} }
if !almostEqual(float64(exp.vals[0].value), float64(v.V)) { if !almostEqual(exp.vals[0].value, v.V) {
return fmt.Errorf("expected %v for %s but got %v", exp.vals[0].value, v.Metric, v.V) return fmt.Errorf("expected %v for %s but got %v", exp.vals[0].value, v.Metric, v.V)
} }
@ -485,7 +481,7 @@ func (t *Test) exec(tc testCommand) error {
if cmd.fail { if cmd.fail {
return nil return nil
} }
return fmt.Errorf("error evaluating query: %s", res.Err) return fmt.Errorf("error evaluating query %q: %s", cmd.expr, res.Err)
} }
if res.Err == nil && cmd.fail { if res.Err == nil && cmd.fail {
return fmt.Errorf("expected error evaluating query but got none") return fmt.Errorf("expected error evaluating query but got none")
@ -514,8 +510,7 @@ func (t *Test) clear() {
} }
t.storage = testutil.NewStorage(t) t.storage = testutil.NewStorage(t)
// TODO(fabxc): add back t.queryEngine = NewEngine(t.storage, nil)
// t.queryEngine = NewEngine(t.storage, nil)
t.context, t.cancelCtx = context.WithCancel(context.Background()) t.context, t.cancelCtx = context.WithCancel(context.Background())
} }

169
promql/value.go Normal file
View file

@ -0,0 +1,169 @@
package promql
import (
"fmt"
"strings"
"github.com/prometheus/prometheus/pkg/labels"
)
// Value is a generic interface for values resulting from a query evaluation.
type Value interface {
Type() ValueType
String() string
}
func (Matrix) Type() ValueType { return ValueTypeMatrix }
func (Vector) Type() ValueType { return ValueTypeVector }
func (Scalar) Type() ValueType { return ValueTypeScalar }
func (String) Type() ValueType { return ValueTypeString }
// ValueType describes a type of a value.
type ValueType string
// The valid value types.
const (
ValueTypeNone = "none"
ValueTypeVector = "vector"
ValueTypeScalar = "scalar"
ValueTypeMatrix = "matrix"
ValueTypeString = "string"
)
// String represents a string value.
type String struct {
V string
T int64
}
func (s String) String() string {
return s.V
}
// Scalar is a data point that's explicitly not associated with a metric.
type Scalar struct {
T int64
V float64
}
func (s Scalar) String() string {
return fmt.Sprintf("scalar: %v @[%v]", s.V, s.T)
}
// Series is a stream of data points belonging to a metric.
type Series struct {
Metric labels.Labels
Points []Point
}
func (s Series) String() string {
vals := make([]string, len(s.Points))
for i, v := range s.Points {
vals[i] = v.String()
}
return fmt.Sprintf("%s =>\n%s", s.Metric, strings.Join(vals, "\n"))
}
// Point represents a single data point for a given timestamp.
type Point struct {
T int64
V float64
}
func (p Point) String() string {
return fmt.Sprintf("%f @[%d]", p.V, p.T)
}
// Sample is a single sample belonging to a metric.
type Sample struct {
Point
Metric labels.Labels
}
func (s Sample) String() string {
return fmt.Sprintf("%s => %s", s.Metric, s.Point)
}
// Vector is basically only an alias for model.Samples, but the
// contract is that in a Vector, all Samples have the same timestamp.
type Vector []Sample
func (vec Vector) String() string {
entries := make([]string, len(vec))
for i, s := range vec {
entries[i] = s.String()
}
return strings.Join(entries, "\n")
}
// Matrix is a slice of Seriess that implements sort.Interface and
// has a String method.
type Matrix []Series
func (m Matrix) String() string {
// TODO(fabxc): sort, or can we rely on order from the querier?
strs := make([]string, len(m))
for i, ss := range m {
strs[i] = ss.String()
}
return strings.Join(strs, "\n")
}
// Result holds the resulting value of an execution or an error
// if any occurred.
type Result struct {
Err error
Value Value
}
// Vector returns a Vector if the result value is one. An error is returned if
// the result was an error or the result value is not a Vector.
func (r *Result) Vector() (Vector, error) {
if r.Err != nil {
return nil, r.Err
}
v, ok := r.Value.(Vector)
if !ok {
return nil, fmt.Errorf("query result is not a Vector")
}
return v, nil
}
// Matrix returns a Matrix. An error is returned if
// the result was an error or the result value is not a Matrix.
func (r *Result) Matrix() (Matrix, error) {
if r.Err != nil {
return nil, r.Err
}
v, ok := r.Value.(Matrix)
if !ok {
return nil, fmt.Errorf("query result is not a range Vector")
}
return v, nil
}
// Scalar returns a Scalar value. An error is returned if
// the result was an error or the result value is not a Scalar.
func (r *Result) Scalar() (Scalar, error) {
if r.Err != nil {
return Scalar{}, r.Err
}
v, ok := r.Value.(Scalar)
if !ok {
return Scalar{}, fmt.Errorf("query result is not a Scalar")
}
return v, nil
}
func (r *Result) String() string {
if r.Err != nil {
return r.Err.Error()
}
if r.Value == nil {
return ""
}
return r.Value.String()
}

View file

@ -24,6 +24,7 @@ func Open(path string) (storage.Storage, error) {
} }
func (a adapter) Querier(mint, maxt int64) (storage.Querier, error) { func (a adapter) Querier(mint, maxt int64) (storage.Querier, error) {
// fmt.Println("new querier at", timestamp.Time(mint), timestamp.Time(maxt), maxt-mint)
return querier{q: a.db.Querier(mint, maxt)}, nil return querier{q: a.db.Querier(mint, maxt)}, nil
} }
@ -48,9 +49,7 @@ func (q querier) Select(oms ...*labels.Matcher) storage.SeriesSet {
ms = append(ms, convertMatcher(om)) ms = append(ms, convertMatcher(om))
} }
set := q.q.Select(ms...) return seriesSet{set: q.q.Select(ms...)}
return seriesSet{set: set}
} }
func (q querier) LabelValues(name string) ([]string, error) { return q.q.LabelValues(name) } func (q querier) LabelValues(name string) ([]string, error) { return q.q.LabelValues(name) }
@ -75,8 +74,11 @@ type appender struct {
a tsdb.Appender a tsdb.Appender
} }
func (a appender) Add(lset labels.Labels, t int64, v float64) { a.a.Add(toTSDBLabels(lset), t, v) } func (a appender) Add(lset labels.Labels, t int64, v float64) {
func (a appender) Commit() error { return a.a.Commit() } // fmt.Println("add", lset, timestamp.Time(t), v)
a.a.Add(toTSDBLabels(lset), t, v)
}
func (a appender) Commit() error { return a.a.Commit() }
func convertMatcher(m *labels.Matcher) tsdbLabels.Matcher { func convertMatcher(m *labels.Matcher) tsdbLabels.Matcher {
switch m.Type { switch m.Type {

View file

@ -4,6 +4,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"github.com/prometheus/common/log"
"github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/storage/tsdb" "github.com/prometheus/prometheus/storage/tsdb"
) )
@ -15,6 +16,9 @@ func NewStorage(t T) storage.Storage {
if err != nil { if err != nil {
t.Fatalf("Opening test dir failed: %s", err) t.Fatalf("Opening test dir failed: %s", err)
} }
log.With("dir", dir).Debugln("opening test storage")
db, err := tsdb.Open(dir) db, err := tsdb.Open(dir)
if err != nil { if err != nil {
t.Fatalf("Opening test storage failed: %s", err) t.Fatalf("Opening test storage failed: %s", err)
@ -28,6 +32,8 @@ type testStorage struct {
} }
func (s testStorage) Close() error { func (s testStorage) Close() error {
log.With("dir", s.dir).Debugln("closing test storage")
if err := s.Storage.Close(); err != nil { if err := s.Storage.Close(); err != nil {
return err return err
} }