@ -43,6 +43,7 @@ import (
"github.com/prometheus/prometheus/model/timestamp"
"github.com/prometheus/prometheus/model/value"
"github.com/prometheus/prometheus/promql/parser"
"github.com/prometheus/prometheus/promql/parser/posrange"
"github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/tsdb/chunkenc"
"github.com/prometheus/prometheus/util/annotations"
@ -1929,20 +1930,20 @@ func (ev *evaluator) eval(ctx context.Context, expr parser.Expr) (parser.Value,
} , e . LHS , e . RHS )
default :
return ev . rangeEval ( ctx , initSignatures , func ( v [ ] parser . Value , sh [ ] [ ] EvalSeriesHelper , enh * EvalNodeHelper ) ( Vector , annotations . Annotations ) {
vec , err := ev . VectorBinop ( e . Op , v [ 0 ] . ( Vector ) , v [ 1 ] . ( Vector ) , e . VectorMatching , e . ReturnBool , sh [ 0 ] , sh [ 1 ] , enh )
vec , err := ev . VectorBinop ( e . Op , v [ 0 ] . ( Vector ) , v [ 1 ] . ( Vector ) , e . VectorMatching , e . ReturnBool , sh [ 0 ] , sh [ 1 ] , enh , e . PositionRange ( ) )
return vec , handleVectorBinopError ( err , e )
} , e . LHS , e . RHS )
}
case lt == parser . ValueTypeVector && rt == parser . ValueTypeScalar :
return ev . rangeEval ( ctx , nil , func ( v [ ] parser . Value , _ [ ] [ ] EvalSeriesHelper , enh * EvalNodeHelper ) ( Vector , annotations . Annotations ) {
vec , err := ev . VectorscalarBinop ( e . Op , v [ 0 ] . ( Vector ) , Scalar { V : v [ 1 ] . ( Vector ) [ 0 ] . F } , false , e . ReturnBool , enh )
vec , err := ev . VectorscalarBinop ( e . Op , v [ 0 ] . ( Vector ) , Scalar { V : v [ 1 ] . ( Vector ) [ 0 ] . F } , false , e . ReturnBool , enh , e . PositionRange ( ) )
return vec , handleVectorBinopError ( err , e )
} , e . LHS , e . RHS )
case lt == parser . ValueTypeScalar && rt == parser . ValueTypeVector :
return ev . rangeEval ( ctx , nil , func ( v [ ] parser . Value , _ [ ] [ ] EvalSeriesHelper , enh * EvalNodeHelper ) ( Vector , annotations . Annotations ) {
vec , err := ev . VectorscalarBinop ( e . Op , v [ 1 ] . ( Vector ) , Scalar { V : v [ 0 ] . ( Vector ) [ 0 ] . F } , true , e . ReturnBool , enh )
vec , err := ev . VectorscalarBinop ( e . Op , v [ 1 ] . ( Vector ) , Scalar { V : v [ 0 ] . ( Vector ) [ 0 ] . F } , true , e . ReturnBool , enh , e . PositionRange ( ) )
return vec , handleVectorBinopError ( err , e )
} , e . LHS , e . RHS )
}
@ -2534,7 +2535,7 @@ func (ev *evaluator) VectorUnless(lhs, rhs Vector, matching *parser.VectorMatchi
}
// VectorBinop evaluates a binary operation between two Vectors, excluding set operators.
func ( ev * evaluator ) VectorBinop ( op parser . ItemType , lhs , rhs Vector , matching * parser . VectorMatching , returnBool bool , lhsh , rhsh [ ] EvalSeriesHelper , enh * EvalNodeHelper ) ( Vector , error ) {
func ( ev * evaluator ) VectorBinop ( op parser . ItemType , lhs , rhs Vector , matching * parser . VectorMatching , returnBool bool , lhsh , rhsh [ ] EvalSeriesHelper , enh * EvalNodeHelper , pos posrange . PositionRange ) ( Vector , error ) {
if matching . Card == parser . CardManyToMany {
panic ( "many-to-many only allowed for set operators" )
}
@ -2608,7 +2609,7 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
fl , fr = fr , fl
hl , hr = hr , hl
}
floatValue , histogramValue , keep , err := vectorElemBinop ( op , fl , fr , hl , hr )
floatValue , histogramValue , keep , err := vectorElemBinop ( op , fl , fr , hl , hr , pos )
if err != nil {
lastErr = err
}
@ -2717,7 +2718,7 @@ func resultMetric(lhs, rhs labels.Labels, op parser.ItemType, matching *parser.V
}
// VectorscalarBinop evaluates a binary operation between a Vector and a Scalar.
func ( ev * evaluator ) VectorscalarBinop ( op parser . ItemType , lhs Vector , rhs Scalar , swap , returnBool bool , enh * EvalNodeHelper ) ( Vector , error ) {
func ( ev * evaluator ) VectorscalarBinop ( op parser . ItemType , lhs Vector , rhs Scalar , swap , returnBool bool , enh * EvalNodeHelper , pos posrange . PositionRange ) ( Vector , error ) {
var lastErr error
for _ , lhsSample := range lhs {
lf , rf := lhsSample . F , rhs . V
@ -2729,7 +2730,7 @@ func (ev *evaluator) VectorscalarBinop(op parser.ItemType, lhs Vector, rhs Scala
lf , rf = rf , lf
lh , rh = rh , lh
}
float , histogram , keep , err := vectorElemBinop ( op , lf , rf , lh , rh )
float , histogram , keep , err := vectorElemBinop ( op , lf , rf , lh , rh , pos )
if err != nil {
lastErr = err
}
@ -2796,7 +2797,7 @@ func scalarBinop(op parser.ItemType, lhs, rhs float64) float64 {
}
// vectorElemBinop evaluates a binary operation between two Vector elements.
func vectorElemBinop ( op parser . ItemType , lhs , rhs float64 , hlhs , hrhs * histogram . FloatHistogram ) ( float64 , * histogram . FloatHistogram , bool , error ) {
func vectorElemBinop ( op parser . ItemType , lhs , rhs float64 , hlhs , hrhs * histogram . FloatHistogram , pos posrange . PositionRange ) ( float64 , * histogram . FloatHistogram , bool , error ) {
switch op {
case parser . ADD :
if hlhs != nil && hrhs != nil {
@ -2806,7 +2807,13 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram
}
return 0 , res . Compact ( 0 ) , true , nil
}
return lhs + rhs , nil , true , nil
if hlhs == nil && hrhs == nil {
return lhs + rhs , nil , true , nil
}
if hlhs != nil {
return 0 , nil , false , annotations . NewIncompatibleTypesInBinOpInfo ( "histogram" , "+" , "float" , pos )
}
return 0 , nil , false , annotations . NewIncompatibleTypesInBinOpInfo ( "float" , "+" , "histogram" , pos )
case parser . SUB :
if hlhs != nil && hrhs != nil {
res , err := hlhs . Copy ( ) . Sub ( hrhs )
@ -2815,7 +2822,13 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram
}
return 0 , res . Compact ( 0 ) , true , nil
}
return lhs - rhs , nil , true , nil
if hlhs == nil && hrhs == nil {
return lhs - rhs , nil , true , nil
}
if hlhs != nil {
return 0 , nil , false , annotations . NewIncompatibleTypesInBinOpInfo ( "histogram" , "-" , "float" , pos )
}
return 0 , nil , false , annotations . NewIncompatibleTypesInBinOpInfo ( "float" , "-" , "histogram" , pos )
case parser . MUL :
if hlhs != nil && hrhs == nil {
return 0 , hlhs . Copy ( ) . Mul ( rhs ) , true , nil
@ -2823,11 +2836,20 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram
if hlhs == nil && hrhs != nil {
return 0 , hrhs . Copy ( ) . Mul ( lhs ) , true , nil
}
if hlhs != nil && hrhs != nil {
return 0 , nil , false , annotations . NewIncompatibleTypesInBinOpInfo ( "histogram" , "*" , "histogram" , pos )
}
return lhs * rhs , nil , true , nil
case parser . DIV :
if hlhs != nil && hrhs == nil {
return 0 , hlhs . Copy ( ) . Div ( rhs ) , true , nil
}
if hrhs != nil {
if hlhs != nil {
return 0 , nil , false , annotations . NewIncompatibleTypesInBinOpInfo ( "histogram" , "/" , "histogram" , pos )
}
return 0 , nil , false , annotations . NewIncompatibleTypesInBinOpInfo ( "float" , "/" , "histogram" , pos )
}
return lhs / rhs , nil , true , nil
case parser . POW :
return math . Pow ( lhs , rhs ) , nil , true , nil
@ -3365,6 +3387,9 @@ func handleVectorBinopError(err error, e *parser.BinaryExpr) annotations.Annotat
}
metricName := ""
pos := e . PositionRange ( )
if errors . Is ( err , annotations . PromQLInfo ) || errors . Is ( err , annotations . PromQLWarning ) {
return annotations . New ( ) . Add ( err )
}
if errors . Is ( err , histogram . ErrHistogramsIncompatibleSchema ) {
return annotations . New ( ) . Add ( annotations . NewMixedExponentialCustomHistogramsWarning ( metricName , pos ) )
} else if errors . Is ( err , histogram . ErrHistogramsIncompatibleBounds ) {