promql: Remove scalar/scalar comparisons.

This change is breaking, use the 'bool' modifier for such comprisons.

After this change all comparisons without 'bool' will filter, and all
comparisons with 'bool' will return 0/1. This makes the language more
consistent and orthogonal, and ultimately easier to learn and use.

If we ever figure out sane semantics for filtering scalar/scalar
comparisons we can add them in, which will most likely come out of how
the new vector() function is used.
This commit is contained in:
Brian Brazil 2015-10-10 16:19:14 +01:00
parent 965a71dc4d
commit c36961130b
4 changed files with 33 additions and 23 deletions

View File

@ -60,6 +60,17 @@ func (i itemType) isAggregator() bool { return i > aggregatorsStart && i < aggre
// Returns false otherwise. // Returns false otherwise.
func (i itemType) isKeyword() bool { return i > keywordsStart && i < keywordsEnd } func (i itemType) isKeyword() bool { return i > keywordsStart && i < keywordsEnd }
// isCompairsonOperator returns true if the item corresponds to a comparison operator.
// Returns false otherwise.
func (i itemType) isComparisonOperator() bool {
switch i {
case itemEQL, itemNEQ, itemLTE, itemLSS, itemGTE, itemGTR:
return true
default:
return false
}
}
// Constants for operator precedence in expressions. // Constants for operator precedence in expressions.
// //
const LowestPrec = 0 // Non-operators. const LowestPrec = 0 // Non-operators.

View File

@ -488,10 +488,7 @@ func (p *parser) expr() Expr {
returnBool := false returnBool := false
// Parse bool modifier. // Parse bool modifier.
if p.peek().typ == itemBool { if p.peek().typ == itemBool {
switch op { if !op.isComparisonOperator() {
case itemEQL, itemNEQ, itemLTE, itemLSS, itemGTE, itemGTR:
break
default:
p.errorf("bool modifier can only be used on comparison operators") p.errorf("bool modifier can only be used on comparison operators")
} }
p.next() p.next()
@ -540,6 +537,9 @@ func (p *parser) expr() Expr {
}, },
VectorMatching: lhs.VectorMatching, VectorMatching: lhs.VectorMatching,
} }
if op.isComparisonOperator() && !returnBool && rhs.Type() == model.ValScalar && lhs.RHS.Type() == model.ValScalar {
p.errorf("comparisons between scalars must use BOOL modifier")
}
} else { } else {
expr = &BinaryExpr{ expr = &BinaryExpr{
Op: op, Op: op,
@ -548,8 +548,12 @@ func (p *parser) expr() Expr {
VectorMatching: vecMatching, VectorMatching: vecMatching,
ReturnBool: returnBool, ReturnBool: returnBool,
} }
if op.isComparisonOperator() && !returnBool && rhs.Type() == model.ValScalar && expr.Type() == model.ValScalar {
p.errorf("comparisons between scalars must use BOOL modifier")
} }
} }
}
} }
// unaryExpr parses a unary expression. // unaryExpr parses a unary expression.

View File

@ -85,23 +85,20 @@ var testExpr = []struct {
input: "1 / 1", input: "1 / 1",
expected: &BinaryExpr{itemDIV, &NumberLiteral{1}, &NumberLiteral{1}, nil, false}, expected: &BinaryExpr{itemDIV, &NumberLiteral{1}, &NumberLiteral{1}, nil, false},
}, { }, {
input: "1 == 1", input: "1 == bool 1",
expected: &BinaryExpr{itemEQL, &NumberLiteral{1}, &NumberLiteral{1}, nil, false}, expected: &BinaryExpr{itemEQL, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
}, { }, {
input: "1 != 1", input: "1 != bool 1",
expected: &BinaryExpr{itemNEQ, &NumberLiteral{1}, &NumberLiteral{1}, nil, false}, expected: &BinaryExpr{itemNEQ, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
}, { }, {
input: "1 > 1", input: "1 > bool 1",
expected: &BinaryExpr{itemGTR, &NumberLiteral{1}, &NumberLiteral{1}, nil, false}, expected: &BinaryExpr{itemGTR, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
}, { }, {
input: "1 >= 1", input: "1 >= bool 1",
expected: &BinaryExpr{itemGTE, &NumberLiteral{1}, &NumberLiteral{1}, nil, false}, expected: &BinaryExpr{itemGTE, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
}, { }, {
input: "1 < 1", input: "1 < bool 1",
expected: &BinaryExpr{itemLSS, &NumberLiteral{1}, &NumberLiteral{1}, nil, false}, expected: &BinaryExpr{itemLSS, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
}, {
input: "1 <= 1",
expected: &BinaryExpr{itemLTE, &NumberLiteral{1}, &NumberLiteral{1}, nil, false},
}, { }, {
input: "1 <= bool 1", input: "1 <= bool 1",
expected: &BinaryExpr{itemLTE, &NumberLiteral{1}, &NumberLiteral{1}, nil, true}, expected: &BinaryExpr{itemLTE, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
@ -203,6 +200,10 @@ var testExpr = []struct {
input: "1 and 1", input: "1 and 1",
fail: true, fail: true,
errMsg: "AND and OR not allowed in binary scalar expression", errMsg: "AND and OR not allowed in binary scalar expression",
}, {
input: "1 == 1",
fail: true,
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,

View File

@ -40,12 +40,6 @@ eval instant at 50m SUM(http_requests) BY (job) != bool SUM(http_requests) BY (j
{job="app-server"} 0 {job="app-server"} 0
eval instant at 50m 0 == 1
0
eval instant at 50m 1 == 1
1
eval instant at 50m 0 == bool 1 eval instant at 50m 0 == bool 1
0 0