mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
Merge pull request #659 from prometheus/fabxc/pql/parse-err
Remove `name` arg from `Parse*` functions, enhance parsing errors.
This commit is contained in:
commit
43e291e978
|
@ -294,7 +294,7 @@ func (ng *Engine) Stop() {
|
||||||
|
|
||||||
// NewQuery returns a new query of the given query string.
|
// NewQuery returns a new query of the given query string.
|
||||||
func (ng *Engine) NewQuery(qs string) (Query, error) {
|
func (ng *Engine) NewQuery(qs string) (Query, error) {
|
||||||
stmts, err := ParseStmts("query", qs)
|
stmts, err := ParseStmts(qs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -325,7 +325,7 @@ func (ng *Engine) NewInstantQuery(es string, ts clientmodel.Timestamp) (Query, e
|
||||||
// NewRangeQuery returns an evaluation query for the given time range and with
|
// NewRangeQuery returns an evaluation query for the given time range and with
|
||||||
// the resolution set by the interval.
|
// the resolution set by the interval.
|
||||||
func (ng *Engine) NewRangeQuery(qs string, start, end clientmodel.Timestamp, interval time.Duration) (Query, error) {
|
func (ng *Engine) NewRangeQuery(qs string, start, end clientmodel.Timestamp, interval time.Duration) (Query, error) {
|
||||||
expr, err := ParseExpr("query", qs)
|
expr, err := ParseExpr(qs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,7 +222,6 @@ type Pos int
|
||||||
|
|
||||||
// lexer holds the state of the scanner.
|
// lexer holds the state of the scanner.
|
||||||
type lexer struct {
|
type lexer struct {
|
||||||
name string // The name of the input; used only for error reports.
|
|
||||||
input string // The string being scanned.
|
input string // The string being scanned.
|
||||||
state stateFn // The next lexing function to enter.
|
state stateFn // The next lexing function to enter.
|
||||||
pos Pos // Current position in the input.
|
pos Pos // Current position in the input.
|
||||||
|
@ -298,12 +297,12 @@ func (l *lexer) lineNumber() int {
|
||||||
|
|
||||||
// linePosition reports at which character in the current line
|
// linePosition reports at which character in the current line
|
||||||
// we are on.
|
// we are on.
|
||||||
func (l *lexer) linePosition() Pos {
|
func (l *lexer) linePosition() int {
|
||||||
lb := Pos(strings.LastIndex(l.input[:l.lastPos], "\n"))
|
lb := strings.LastIndex(l.input[:l.lastPos], "\n")
|
||||||
if lb == -1 {
|
if lb == -1 {
|
||||||
return 1 + l.lastPos
|
return 1 + int(l.lastPos)
|
||||||
}
|
}
|
||||||
return 1 + l.lastPos - lb
|
return 1 + int(l.lastPos) - lb
|
||||||
}
|
}
|
||||||
|
|
||||||
// errorf returns an error token and terminates the scan by passing
|
// errorf returns an error token and terminates the scan by passing
|
||||||
|
@ -321,9 +320,8 @@ func (l *lexer) nextItem() item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// lex creates a new scanner for the input string.
|
// lex creates a new scanner for the input string.
|
||||||
func lex(name, input string) *lexer {
|
func lex(input string) *lexer {
|
||||||
l := &lexer{
|
l := &lexer{
|
||||||
name: name,
|
|
||||||
input: input,
|
input: input,
|
||||||
items: make(chan item),
|
items: make(chan item),
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
package promql
|
package promql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -328,8 +327,7 @@ var tests = []struct {
|
||||||
// for the parser to avoid duplicated effort.
|
// for the parser to avoid duplicated effort.
|
||||||
func TestLexer(t *testing.T) {
|
func TestLexer(t *testing.T) {
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
tn := fmt.Sprintf("test.%d \"%s\"", i, test.input)
|
l := lex(test.input)
|
||||||
l := lex(tn, test.input)
|
|
||||||
|
|
||||||
out := []item{}
|
out := []item{}
|
||||||
for it := range l.items {
|
for it := range l.items {
|
||||||
|
@ -339,20 +337,20 @@ func TestLexer(t *testing.T) {
|
||||||
lastItem := out[len(out)-1]
|
lastItem := out[len(out)-1]
|
||||||
if test.fail {
|
if test.fail {
|
||||||
if lastItem.typ != itemError {
|
if lastItem.typ != itemError {
|
||||||
t.Fatalf("%s: expected lexing error but did not fail", tn)
|
t.Fatalf("%d: expected lexing error but did not fail", i)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if lastItem.typ == itemError {
|
if lastItem.typ == itemError {
|
||||||
t.Fatalf("%s: unexpected lexing error: %s", tn, lastItem)
|
t.Fatalf("%d: unexpected lexing error: %s", i, lastItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(lastItem, item{itemEOF, Pos(len(test.input)), ""}) {
|
if !reflect.DeepEqual(lastItem, item{itemEOF, Pos(len(test.input)), ""}) {
|
||||||
t.Fatalf("%s: lexing error: expected output to end with EOF item", tn)
|
t.Fatalf("%d: lexing error: expected output to end with EOF item", i)
|
||||||
}
|
}
|
||||||
out = out[:len(out)-1]
|
out = out[:len(out)-1]
|
||||||
if !reflect.DeepEqual(out, test.expected) {
|
if !reflect.DeepEqual(out, test.expected) {
|
||||||
t.Errorf("%s: lexing mismatch:\nexpected: %#v\n-----\ngot: %#v", tn, test.expected, out)
|
t.Errorf("%d: lexing mismatch:\nexpected: %#v\n-----\ngot: %#v", i, test.expected, out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
clientmodel "github.com/prometheus/client_golang/model"
|
clientmodel "github.com/prometheus/client_golang/model"
|
||||||
|
@ -25,15 +26,29 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type parser struct {
|
type parser struct {
|
||||||
name string
|
|
||||||
lex *lexer
|
lex *lexer
|
||||||
token [3]item
|
token [3]item
|
||||||
peekCount int
|
peekCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseErr wraps a parsing error with line and position context.
|
||||||
|
// If the parsing input was a single line, line will be 0 and omitted
|
||||||
|
// from the error string.
|
||||||
|
type ParseErr struct {
|
||||||
|
Line, Pos int
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ParseErr) Error() string {
|
||||||
|
if e.Line == 0 {
|
||||||
|
return fmt.Sprintf("Parse error at char %d: %s", e.Pos, e.Err)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Parse error at line %d, char %d: %s", e.Line, e.Pos, e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
// ParseStmts parses the input and returns the resulting statements or any ocurring error.
|
// ParseStmts parses the input and returns the resulting statements or any ocurring error.
|
||||||
func ParseStmts(name, input string) (Statements, error) {
|
func ParseStmts(input string) (Statements, error) {
|
||||||
p := newParser(name, input)
|
p := newParser(input)
|
||||||
|
|
||||||
stmts, err := p.parseStmts()
|
stmts, err := p.parseStmts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -44,8 +59,8 @@ func ParseStmts(name, input string) (Statements, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseExpr returns the expression parsed from the input.
|
// ParseExpr returns the expression parsed from the input.
|
||||||
func ParseExpr(name, input string) (Expr, error) {
|
func ParseExpr(input string) (Expr, error) {
|
||||||
p := newParser(name, input)
|
p := newParser(input)
|
||||||
|
|
||||||
expr, err := p.parseExpr()
|
expr, err := p.parseExpr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -56,10 +71,9 @@ func ParseExpr(name, input string) (Expr, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newParser returns a new parser.
|
// newParser returns a new parser.
|
||||||
func newParser(name, input string) *parser {
|
func newParser(input string) *parser {
|
||||||
p := &parser{
|
p := &parser{
|
||||||
name: name,
|
lex: lex(input),
|
||||||
lex: lex(name, input),
|
|
||||||
}
|
}
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
@ -144,13 +158,20 @@ func (p *parser) backup() {
|
||||||
|
|
||||||
// errorf formats the error and terminates processing.
|
// errorf formats the error and terminates processing.
|
||||||
func (p *parser) errorf(format string, args ...interface{}) {
|
func (p *parser) errorf(format string, args ...interface{}) {
|
||||||
format = fmt.Sprintf("%s:%d,%d %s", p.name, p.lex.lineNumber(), p.lex.linePosition(), format)
|
p.error(fmt.Errorf(format, args...))
|
||||||
panic(fmt.Errorf(format, args...))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// error terminates processing.
|
// error terminates processing.
|
||||||
func (p *parser) error(err error) {
|
func (p *parser) error(err error) {
|
||||||
p.errorf("%s", err)
|
perr := &ParseErr{
|
||||||
|
Line: p.lex.lineNumber(),
|
||||||
|
Pos: p.lex.linePosition(),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
if strings.Count(strings.TrimSpace(p.lex.input), "\n") == 0 {
|
||||||
|
perr.Line = 0
|
||||||
|
}
|
||||||
|
panic(perr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// expect consumes the next token and guarantees it has the required type.
|
// expect consumes the next token and guarantees it has the required type.
|
||||||
|
|
|
@ -785,7 +785,7 @@ var testExpr = []struct {
|
||||||
func TestParseExpressions(t *testing.T) {
|
func TestParseExpressions(t *testing.T) {
|
||||||
for _, test := range testExpr {
|
for _, test := range testExpr {
|
||||||
|
|
||||||
parser := newParser("test", test.input)
|
parser := newParser(test.input)
|
||||||
|
|
||||||
expr, err := parser.parseExpr()
|
expr, err := parser.parseExpr()
|
||||||
if !test.fail && err != nil {
|
if !test.fail && err != nil {
|
||||||
|
@ -819,7 +819,7 @@ func TestParseExpressions(t *testing.T) {
|
||||||
|
|
||||||
// NaN has no equality. Thus, we need a separate test for it.
|
// NaN has no equality. Thus, we need a separate test for it.
|
||||||
func TestNaNExpression(t *testing.T) {
|
func TestNaNExpression(t *testing.T) {
|
||||||
parser := newParser("test", "NaN")
|
parser := newParser("NaN")
|
||||||
|
|
||||||
expr, err := parser.parseExpr()
|
expr, err := parser.parseExpr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1028,7 +1028,7 @@ var testStatement = []struct {
|
||||||
|
|
||||||
func TestParseStatements(t *testing.T) {
|
func TestParseStatements(t *testing.T) {
|
||||||
for _, test := range testStatement {
|
for _, test := range testStatement {
|
||||||
parser := newParser("test", test.input)
|
parser := newParser(test.input)
|
||||||
|
|
||||||
stmts, err := parser.parseStmts()
|
stmts, err := parser.parseStmts()
|
||||||
if !test.fail && err != nil {
|
if !test.fail && err != nil {
|
||||||
|
|
|
@ -173,7 +173,7 @@ func TestAlertingRule(t *testing.T) {
|
||||||
engine := promql.NewEngine(storage)
|
engine := promql.NewEngine(storage)
|
||||||
defer engine.Stop()
|
defer engine.Stop()
|
||||||
|
|
||||||
expr, err := promql.ParseExpr("test", `http_requests{group="canary", job="app-server"} < 100`)
|
expr, err := promql.ParseExpr(`http_requests{group="canary", job="app-server"} < 100`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unable to parse alert expression: %s", err)
|
t.Fatalf("Unable to parse alert expression: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ func checkRules(filename string, in io.Reader, out io.Writer) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rules, err := promql.ParseStmts(filename, string(content))
|
rules, err := promql.ParseStmts(string(content))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ func TestQuery(t *testing.T) {
|
||||||
{
|
{
|
||||||
queryStr: "",
|
queryStr: "",
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
bodyRe: `{"type":"error","value":"query:1,1 no expression found in input","version":1}`,
|
bodyRe: `{"type":"error","value":"Parse error at char 1: no expression found in input","version":1}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
queryStr: "expr=testmetric",
|
queryStr: "expr=testmetric",
|
||||||
|
@ -77,7 +77,7 @@ func TestQuery(t *testing.T) {
|
||||||
{
|
{
|
||||||
queryStr: "expr=(badexpression",
|
queryStr: "expr=(badexpression",
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
bodyRe: `{"type":"error","value":"query:1,15 unexpected unclosed left parenthesis in paren expression","version":1}`,
|
bodyRe: `{"type":"error","value":"Parse error at char 15: unexpected unclosed left parenthesis in paren expression","version":1}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue