This commit is contained in:
Aliaksandr Valialkin 2024-05-23 13:58:30 +02:00
parent e3cbf97bdd
commit a686c7dd74
No known key found for this signature in database
GPG key ID: 52C003EE2BCDB9EB
3 changed files with 56 additions and 44 deletions

View file

@ -5,8 +5,6 @@ import (
"html" "html"
"strconv" "strconv"
"strings" "strings"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
) )
// pattern represents text pattern in the form 'some_text<some_field>other_text...' // pattern represents text pattern in the form 'some_text<some_field>other_text...'
@ -28,18 +26,25 @@ type patternField struct {
type patternStep struct { type patternStep struct {
prefix string prefix string
field string
opt string field string
fieldOpt string
} }
func (ptn *pattern) clone() *pattern { func (ptn *pattern) clone() *pattern {
steps := ptn.steps matches := make([]string, len(ptn.steps))
fields, matches := newFieldsAndMatchesFromPatternSteps(steps) var fields []patternField
if len(fields) == 0 { for i, step := range ptn.steps {
logger.Panicf("BUG: fields cannot be empty for steps=%v", steps) if step.field != "" {
fields = append(fields, patternField{
name: step.field,
value: &matches[i],
})
}
} }
return &pattern{ return &pattern{
steps: steps, steps: ptn.steps,
matches: matches, matches: matches,
fields: fields, fields: fields,
} }
@ -59,7 +64,18 @@ func parsePattern(s string) (*pattern, error) {
} }
// Build pattern struct // Build pattern struct
fields, matches := newFieldsAndMatchesFromPatternSteps(steps)
matches := make([]string, len(steps))
var fields []patternField
for i, step := range steps {
if step.field != "" {
fields = append(fields, patternField{
name: step.field,
value: &matches[i],
})
}
}
if len(fields) == 0 { if len(fields) == 0 {
return nil, fmt.Errorf("pattern %q must contain at least a single named field in the form <field_name>", s) return nil, fmt.Errorf("pattern %q must contain at least a single named field in the form <field_name>", s)
} }
@ -72,35 +88,17 @@ func parsePattern(s string) (*pattern, error) {
return ptn, nil return ptn, nil
} }
func newFieldsAndMatchesFromPatternSteps(steps []patternStep) ([]patternField, []string) {
matches := make([]string, len(steps))
var fields []patternField
for i, step := range steps {
if step.field != "" {
fields = append(fields, patternField{
name: step.field,
value: &matches[i],
})
}
}
return fields, matches
}
func (ptn *pattern) apply(s string) { func (ptn *pattern) apply(s string) {
clear(ptn.matches) clear(ptn.matches)
steps := ptn.steps steps := ptn.steps
if prefix := steps[0].prefix; prefix != "" { n, prefixLen := prefixIndex(s, steps[0].prefix)
n := strings.Index(s, prefix) if n < 0 {
if n < 0 { // Mismatch
// Mismatch return
return
}
s = s[n+len(prefix):]
} }
s = s[n+prefixLen:]
matches := ptn.matches matches := ptn.matches
for i := range steps { for i := range steps {
@ -109,7 +107,7 @@ func (ptn *pattern) apply(s string) {
nextPrefix = steps[i+1].prefix nextPrefix = steps[i+1].prefix
} }
us, nOffset := tryUnquoteString(s, steps[i].opt) us, nOffset := tryUnquoteString(s, steps[i].fieldOpt)
if nOffset >= 0 { if nOffset >= 0 {
// Matched quoted string // Matched quoted string
matches[i] = us matches[i] = us
@ -125,17 +123,28 @@ func (ptn *pattern) apply(s string) {
matches[i] = s matches[i] = s
return return
} }
n := strings.Index(s, nextPrefix) n, prefixLen := prefixIndex(s, nextPrefix)
if n < 0 { if n < 0 {
// Mismatch // Mismatch
return return
} }
matches[i] = s[:n] matches[i] = s[:n]
s = s[n+len(nextPrefix):] s = s[n+prefixLen:]
} }
} }
} }
func prefixIndex(s, prefix string) (int, int) {
if len(prefix) == 0 {
return 0, 0
}
n := strings.Index(s, prefix)
if n < 0 {
return -1, 0
}
return n, len(prefix)
}
func tryUnquoteString(s, opt string) (string, int) { func tryUnquoteString(s, opt string) (string, int) {
if opt == "plain" { if opt == "plain" {
return "", -1 return "", -1
@ -163,7 +172,7 @@ func parsePatternSteps(s string) ([]patternStep, error) {
return nil, err return nil, err
} }
// Unescape prefixes // unescape prefixes
for i := range steps { for i := range steps {
step := &steps[i] step := &steps[i]
step.prefix = html.UnescapeString(step.prefix) step.prefix = html.UnescapeString(step.prefix)
@ -174,7 +183,7 @@ func parsePatternSteps(s string) ([]patternStep, error) {
step := &steps[i] step := &steps[i]
field := step.field field := step.field
if n := strings.IndexByte(field, ':'); n >= 0 { if n := strings.IndexByte(field, ':'); n >= 0 {
step.opt = strings.TrimSpace(field[:n]) step.fieldOpt = strings.TrimSpace(field[:n])
field = field[n+1:] field = field[n+1:]
} }
step.field = strings.TrimSpace(field) step.field = strings.TrimSpace(field)

View file

@ -63,6 +63,9 @@ func TestPatternApply(t *testing.T) {
f(`foo=<bar> `, "foo=`bar baz,abc` def", []string{"bar baz,abc"}) f(`foo=<bar> `, "foo=`bar baz,abc` def", []string{"bar baz,abc"})
f(`<foo>`, `"foo,\"bar"`, []string{`foo,"bar`}) f(`<foo>`, `"foo,\"bar"`, []string{`foo,"bar`})
f(`<foo>,"bar`, `"foo,\"bar"`, []string{`foo,"bar`}) f(`<foo>,"bar`, `"foo,\"bar"`, []string{`foo,"bar`})
// disable automatic unquoting of quoted field
f(`[<plain:foo>]`, `["foo","bar"]`, []string{`"foo","bar"`})
} }
func TestParsePatternFailure(t *testing.T) { func TestParsePatternFailure(t *testing.T) {
@ -207,13 +210,13 @@ func TestParsePatternStepsSuccess(t *testing.T) {
}) })
f("< q : foo >bar<plain : baz:c:y>f<:foo:bar:baz>", []patternStep{ f("< q : foo >bar<plain : baz:c:y>f<:foo:bar:baz>", []patternStep{
{ {
field: "foo", field: "foo",
opt: "q", fieldOpt: "q",
}, },
{ {
prefix: "bar", prefix: "bar",
field: "baz:c:y", field: "baz:c:y",
opt: "plain", fieldOpt: "plain",
}, },
{ {
prefix: "f", prefix: "f",

View file

@ -137,7 +137,7 @@ func (shard *pipeFormatProcessorShard) formatRow(pf *pipeFormat, br *blockResult
if step.field != "" { if step.field != "" {
c := br.getColumnByName(step.field) c := br.getColumnByName(step.field)
v := c.getValueAtRow(br, rowIdx) v := c.getValueAtRow(br, rowIdx)
if step.opt == "q" { if step.fieldOpt == "q" {
b = strconv.AppendQuote(b, v) b = strconv.AppendQuote(b, v)
} else { } else {
b = append(b, v...) b = append(b, v...)