@@ -17,6 +17,7 @@ import (
17
17
"fmt"
18
18
"sort"
19
19
"strings"
20
+ "unicode/utf8"
20
21
21
22
"github.com/pkg/errors"
22
23
"github.com/prometheus/tsdb/chunkenc"
@@ -266,6 +267,62 @@ func (q *blockQuerier) Close() error {
266
267
return merr .Err ()
267
268
}
268
269
270
+ // Bitmap used by func isRegexMetaCharacter to check whether a character needs to be escaped.
271
+ var regexMetaCharacterBytes [16 ]byte
272
+
273
+ // isRegexMetaCharacter reports whether byte b needs to be escaped.
274
+ func isRegexMetaCharacter (b byte ) bool {
275
+ return b < utf8 .RuneSelf && regexMetaCharacterBytes [b % 16 ]& (1 << (b / 16 )) != 0
276
+ }
277
+
278
+ func init () {
279
+ for _ , b := range []byte (`.+*?()|[]{}^$` ) {
280
+ regexMetaCharacterBytes [b % 16 ] |= 1 << (b / 16 )
281
+ }
282
+ }
283
+
284
+ func findSetMatches (pattern string ) []string {
285
+ // Return empty matches if the wrapper from Prometheus is missing.
286
+ if len (pattern ) < 6 || pattern [:4 ] != "^(?:" || pattern [len (pattern )- 2 :] != ")$" {
287
+ return nil
288
+ }
289
+ escaped := false
290
+ sets := []* strings.Builder {& strings.Builder {}}
291
+ for i := 4 ; i < len (pattern )- 2 ; i ++ {
292
+ if escaped {
293
+ switch {
294
+ case isRegexMetaCharacter (pattern [i ]):
295
+ sets [len (sets )- 1 ].WriteByte (pattern [i ])
296
+ case pattern [i ] == '\\' :
297
+ sets [len (sets )- 1 ].WriteByte ('\\' )
298
+ default :
299
+ return nil
300
+ }
301
+ escaped = false
302
+ } else {
303
+ switch {
304
+ case isRegexMetaCharacter (pattern [i ]):
305
+ if pattern [i ] == '|' {
306
+ sets = append (sets , & strings.Builder {})
307
+ } else {
308
+ return nil
309
+ }
310
+ case pattern [i ] == '\\' :
311
+ escaped = true
312
+ default :
313
+ sets [len (sets )- 1 ].WriteByte (pattern [i ])
314
+ }
315
+ }
316
+ }
317
+ matches := make ([]string , 0 , len (sets ))
318
+ for _ , s := range sets {
319
+ if s .Len () > 0 {
320
+ matches = append (matches , s .String ())
321
+ }
322
+ }
323
+ return matches
324
+ }
325
+
269
326
// PostingsForMatchers assembles a single postings iterator against the index reader
270
327
// based on the given matchers.
271
328
func PostingsForMatchers (ix IndexReader , ms ... labels.Matcher ) (index.Postings , error ) {
@@ -346,6 +403,14 @@ func postingsForMatcher(ix IndexReader, m labels.Matcher) (index.Postings, error
346
403
return ix .Postings (em .Name (), em .Value ())
347
404
}
348
405
406
+ // Fast-path for set matching.
407
+ if em , ok := m .(* labels.RegexpMatcher ); ok {
408
+ setMatches := findSetMatches (em .Value ())
409
+ if len (setMatches ) > 0 {
410
+ return postingsForSetMatcher (ix , em .Name (), setMatches )
411
+ }
412
+ }
413
+
349
414
tpls , err := ix .LabelValues (m .Name ())
350
415
if err != nil {
351
416
return nil , err
@@ -411,6 +476,18 @@ func inversePostingsForMatcher(ix IndexReader, m labels.Matcher) (index.Postings
411
476
return index .Merge (rit ... ), nil
412
477
}
413
478
479
+ func postingsForSetMatcher (ix IndexReader , name string , matches []string ) (index.Postings , error ) {
480
+ var its []index.Postings
481
+ for _ , match := range matches {
482
+ if it , err := ix .Postings (name , match ); err == nil {
483
+ its = append (its , it )
484
+ } else {
485
+ return nil , err
486
+ }
487
+ }
488
+ return index .Merge (its ... ), nil
489
+ }
490
+
414
491
func mergeStrings (a , b []string ) []string {
415
492
maxl := len (a )
416
493
if len (b ) > len (a ) {
0 commit comments