github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/logql/syntax/parser.go (about) 1 package syntax 2 3 import ( 4 "errors" 5 "fmt" 6 "sort" 7 "strings" 8 "sync" 9 "text/scanner" 10 11 "github.com/prometheus/prometheus/model/labels" 12 promql_parser "github.com/prometheus/prometheus/promql/parser" 13 14 "github.com/grafana/loki/pkg/logqlmodel" 15 "github.com/grafana/loki/pkg/util" 16 ) 17 18 const ( 19 EmptyMatchers = "{}" 20 21 errAtleastOneEqualityMatcherRequired = "queries require at least one regexp or equality matcher that does not have an empty-compatible value. For instance, app=~\".*\" does not meet this requirement, but app=~\".+\" will" 22 ) 23 24 var parserPool = sync.Pool{ 25 New: func() interface{} { 26 p := &parser{ 27 p: &exprParserImpl{}, 28 Reader: strings.NewReader(""), 29 lexer: &lexer{}, 30 } 31 return p 32 }, 33 } 34 35 const maxInputSize = 5120 36 37 func init() { 38 // Improve the error messages coming out of yacc. 39 exprErrorVerbose = true 40 // uncomment when you need to understand yacc rule tree. 41 // exprDebug = 3 42 for str, tok := range tokens { 43 exprToknames[tok-exprPrivate+1] = str 44 } 45 } 46 47 type parser struct { 48 p *exprParserImpl 49 *lexer 50 expr Expr 51 *strings.Reader 52 } 53 54 func (p *parser) Parse() (Expr, error) { 55 p.lexer.errs = p.lexer.errs[:0] 56 p.lexer.Scanner.Error = func(_ *scanner.Scanner, msg string) { 57 p.lexer.Error(msg) 58 } 59 e := p.p.Parse(p) 60 if e != 0 || len(p.lexer.errs) > 0 { 61 return nil, p.lexer.errs[0] 62 } 63 return p.expr, nil 64 } 65 66 // ParseExpr parses a string and returns an Expr. 67 func ParseExpr(input string) (Expr, error) { 68 expr, err := parseExprWithoutValidation(input) 69 if err != nil { 70 return nil, err 71 } 72 if err := validateExpr(expr); err != nil { 73 return nil, err 74 } 75 return expr, nil 76 } 77 78 func parseExprWithoutValidation(input string) (expr Expr, err error) { 79 if len(input) >= maxInputSize { 80 return nil, logqlmodel.NewParseError(fmt.Sprintf("input size too long (%d > %d)", len(input), maxInputSize), 0, 0) 81 } 82 83 defer func() { 84 if r := recover(); r != nil { 85 var ok bool 86 if err, ok = r.(error); ok { 87 if errors.Is(err, logqlmodel.ErrParse) { 88 return 89 } 90 err = logqlmodel.NewParseError(err.Error(), 0, 0) 91 } 92 } 93 }() 94 95 p := parserPool.Get().(*parser) 96 defer parserPool.Put(p) 97 98 p.Reader.Reset(input) 99 p.lexer.Init(p.Reader) 100 return p.Parse() 101 } 102 103 func validateExpr(expr Expr) error { 104 switch e := expr.(type) { 105 case SampleExpr: 106 err := validateSampleExpr(e) 107 if err != nil { 108 return err 109 } 110 case LogSelectorExpr: 111 err := validateMatchers(e.Matchers()) 112 if err != nil { 113 return err 114 } 115 default: 116 return logqlmodel.NewParseError(fmt.Sprintf("unexpected expression type: %v", e), 0, 0) 117 } 118 return nil 119 } 120 121 // validateMatchers checks whether a query would touch all the streams in the query range or uses at least one matcher to select specific streams. 122 func validateMatchers(matchers []*labels.Matcher) error { 123 if len(matchers) == 0 { 124 return nil 125 } 126 _, matchers = util.SplitFiltersAndMatchers(matchers) 127 if len(matchers) == 0 { 128 return logqlmodel.NewParseError(errAtleastOneEqualityMatcherRequired, 0, 0) 129 } 130 131 return nil 132 } 133 134 // ParseMatchers parses a string and returns labels matchers, if the expression contains 135 // anything else it will return an error. 136 func ParseMatchers(input string) ([]*labels.Matcher, error) { 137 expr, err := ParseExpr(input) 138 if err != nil { 139 return nil, err 140 } 141 matcherExpr, ok := expr.(*MatchersExpr) 142 if !ok { 143 return nil, errors.New("only label matchers is supported") 144 } 145 return matcherExpr.Mts, nil 146 } 147 148 func MatchersString(xs []*labels.Matcher) string { 149 return newMatcherExpr(xs).String() 150 } 151 152 // ParseSampleExpr parses a string and returns the sampleExpr 153 func ParseSampleExpr(input string) (SampleExpr, error) { 154 expr, err := ParseExpr(input) 155 if err != nil { 156 return nil, err 157 } 158 sampleExpr, ok := expr.(SampleExpr) 159 if !ok { 160 return nil, errors.New("only sample expression supported") 161 } 162 163 return sampleExpr, nil 164 } 165 166 func validateSampleExpr(expr SampleExpr) error { 167 switch e := expr.(type) { 168 case *BinOpExpr: 169 if err := validateSampleExpr(e.SampleExpr); err != nil { 170 return err 171 } 172 173 return validateSampleExpr(e.RHS) 174 case *LiteralExpr: 175 return nil 176 default: 177 return validateMatchers(expr.Selector().Matchers()) 178 } 179 } 180 181 // ParseLogSelector parses a log selector expression `{app="foo"} |= "filter"` 182 func ParseLogSelector(input string, validate bool) (LogSelectorExpr, error) { 183 expr, err := parseExprWithoutValidation(input) 184 if err != nil { 185 return nil, err 186 } 187 logSelector, ok := expr.(LogSelectorExpr) 188 if !ok { 189 return nil, errors.New("only log selector is supported") 190 } 191 if validate { 192 if err := validateExpr(expr); err != nil { 193 return nil, err 194 } 195 } 196 return logSelector, nil 197 } 198 199 // ParseLabels parses labels from a string using logql parser. 200 func ParseLabels(lbs string) (labels.Labels, error) { 201 ls, err := promql_parser.ParseMetric(lbs) 202 if err != nil { 203 return nil, err 204 } 205 sort.Sort(ls) 206 return ls, nil 207 }