mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-31 15:06:26 +00:00
wip
This commit is contained in:
parent
38b9213a0c
commit
fde91c8f97
4 changed files with 96 additions and 18 deletions
|
@ -1544,6 +1544,16 @@ func TestQueryGetNeededColumns(t *testing.T) {
|
||||||
f(`* | fields x,y | field_names as bar | fields baz`, `x,y`, ``)
|
f(`* | fields x,y | field_names as bar | fields baz`, `x,y`, ``)
|
||||||
f(`* | rm x,y | field_names as bar | fields baz`, `*`, `x,y`)
|
f(`* | rm x,y | field_names as bar | fields baz`, `*`, `x,y`)
|
||||||
|
|
||||||
|
f(`* | extract from s1 "<f1>x<f2>"`, `*`, ``)
|
||||||
|
f(`* | extract from s1 "<f1>x<f2>" | fields foo`, `foo`, ``)
|
||||||
|
f(`* | extract from s1 "<f1>x<f2>" | fields foo,s1`, `foo,s1`, ``)
|
||||||
|
f(`* | extract from s1 "<f1>x<f2>" | fields foo,f1`, `foo,s1`, ``)
|
||||||
|
f(`* | extract from s1 "<f1>x<f2>" | fields foo,f1,f2`, `foo,s1`, ``)
|
||||||
|
f(`* | extract from s1 "<f1>x<f2>" | rm foo`, `*`, `foo`)
|
||||||
|
f(`* | extract from s1 "<f1>x<f2>" | rm foo,s1`, `*`, `foo`)
|
||||||
|
f(`* | extract from s1 "<f1>x<f2>" | rm foo,f1`, `*`, `foo`)
|
||||||
|
f(`* | extract from s1 "<f1>x<f2>" | rm foo,f1,f2`, `*`, `foo,s1`)
|
||||||
|
|
||||||
f(`* | rm f1, f2`, `*`, `f1,f2`)
|
f(`* | rm f1, f2`, `*`, `f1,f2`)
|
||||||
f(`* | rm f1, f2 | mv f2 f3`, `*`, `f1,f2,f3`)
|
f(`* | rm f1, f2 | mv f2 f3`, `*`, `f1,f2,f3`)
|
||||||
f(`* | rm f1, f2 | cp f2 f3`, `*`, `f1,f2,f3`)
|
f(`* | rm f1, f2 | cp f2 f3`, `*`, `f1,f2,f3`)
|
||||||
|
|
|
@ -10,14 +10,14 @@ import (
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// pipeExtract processes '| extract (field, format)' pipe.
|
// pipeExtract processes '| extract from <field> <pattern>' pipe.
|
||||||
//
|
//
|
||||||
// See https://docs.victoriametrics.com/victorialogs/logsql/#extract-pipe
|
// See https://docs.victoriametrics.com/victorialogs/logsql/#extract-pipe
|
||||||
type pipeExtract struct {
|
type pipeExtract struct {
|
||||||
fromField string
|
fromField string
|
||||||
steps []extractFormatStep
|
steps []extractFormatStep
|
||||||
|
|
||||||
format string
|
pattern string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pe *pipeExtract) String() string {
|
func (pe *pipeExtract) String() string {
|
||||||
|
@ -25,16 +25,39 @@ func (pe *pipeExtract) String() string {
|
||||||
if !isMsgFieldName(pe.fromField) {
|
if !isMsgFieldName(pe.fromField) {
|
||||||
s += " from " + quoteTokenIfNeeded(pe.fromField)
|
s += " from " + quoteTokenIfNeeded(pe.fromField)
|
||||||
}
|
}
|
||||||
s += " " + quoteTokenIfNeeded(pe.format)
|
s += " " + quoteTokenIfNeeded(pe.pattern)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pe *pipeExtract) updateNeededFields(neededFields, unneededFields fieldsSet) {
|
func (pe *pipeExtract) updateNeededFields(neededFields, unneededFields fieldsSet) {
|
||||||
neededFields.add(pe.fromField)
|
if neededFields.contains("*") {
|
||||||
|
needFromField := false
|
||||||
for _, step := range pe.steps {
|
for _, step := range pe.steps {
|
||||||
if step.field != "" {
|
if step.field != "" {
|
||||||
unneededFields.remove(step.field)
|
if !unneededFields.contains(step.field) {
|
||||||
|
needFromField = true
|
||||||
|
} else {
|
||||||
|
unneededFields.remove(step.field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if needFromField {
|
||||||
|
unneededFields.remove(pe.fromField)
|
||||||
|
} else {
|
||||||
|
unneededFields.add(pe.fromField)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
needFromField := false
|
||||||
|
for _, step := range pe.steps {
|
||||||
|
if step.field != "" {
|
||||||
|
if neededFields.contains(step.field) {
|
||||||
|
needFromField = true
|
||||||
|
neededFields.remove(step.field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if needFromField {
|
||||||
|
neededFields.add(pe.fromField)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,19 +169,19 @@ func parsePipeExtract(lex *lexer) (*pipeExtract, error) {
|
||||||
fromField = f
|
fromField = f
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := getCompoundToken(lex)
|
pattern, err := getCompoundToken(lex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot read 'format': %w", err)
|
return nil, fmt.Errorf("cannot read 'pattern': %w", err)
|
||||||
}
|
}
|
||||||
steps, err := parseExtractFormatSteps(format)
|
steps, err := parseExtractFormatSteps(pattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot parse 'format' %q: %w", format, err)
|
return nil, fmt.Errorf("cannot parse 'pattern' %q: %w", pattern, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pe := &pipeExtract{
|
pe := &pipeExtract{
|
||||||
fromField: fromField,
|
fromField: fromField,
|
||||||
steps: steps,
|
steps: steps,
|
||||||
format: format,
|
pattern: pattern,
|
||||||
}
|
}
|
||||||
return pe, nil
|
return pe, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestExtractFormatApply(t *testing.T) {
|
func TestExtractFormatApply(t *testing.T) {
|
||||||
f := func(format, s string, resultsExpected []string) {
|
f := func(pattern, s string, resultsExpected []string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
steps, err := parseExtractFormatSteps(format)
|
steps, err := parseExtractFormatSteps(pattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %s", err)
|
t.Fatalf("unexpected error: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ func TestExtractFormatApply(t *testing.T) {
|
||||||
|
|
||||||
f("ip=<ip> <> path=<path> ", "x=a, ip=1.2.3.4 method=GET host='abc' path=/foo/bar some tail here", []string{"1.2.3.4", "/foo/bar"})
|
f("ip=<ip> <> path=<path> ", "x=a, ip=1.2.3.4 method=GET host='abc' path=/foo/bar some tail here", []string{"1.2.3.4", "/foo/bar"})
|
||||||
|
|
||||||
// escaped format
|
// escaped pattern
|
||||||
f("ip=<<ip>>", "foo ip=<1.2.3.4> bar", []string{"1.2.3.4"})
|
f("ip=<<ip>>", "foo ip=<1.2.3.4> bar", []string{"1.2.3.4"})
|
||||||
f("ip=<<ip>>", "foo ip=<foo&bar> bar", []string{"foo&bar"})
|
f("ip=<<ip>>", "foo ip=<foo&bar> bar", []string{"foo&bar"})
|
||||||
|
|
||||||
|
@ -177,3 +177,48 @@ func TestParseExtractFormatStepFailure(t *testing.T) {
|
||||||
f("<foo")
|
f("<foo")
|
||||||
f("foo<bar")
|
f("foo<bar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPipeExtractUpdateNeededFields(t *testing.T) {
|
||||||
|
f := func(s string, neededFields, unneededFields, neededFieldsExpected, unneededFieldsExpected string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
nfs := newTestFieldsSet(neededFields)
|
||||||
|
unfs := newTestFieldsSet(unneededFields)
|
||||||
|
|
||||||
|
lex := newLexer(s)
|
||||||
|
p, err := parsePipeExtract(lex)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot parse %s: %s", s, err)
|
||||||
|
}
|
||||||
|
p.updateNeededFields(nfs, unfs)
|
||||||
|
|
||||||
|
assertNeededFields(t, nfs, unfs, neededFieldsExpected, unneededFieldsExpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// all the needed fields
|
||||||
|
f("extract from x '<foo>'", "*", "", "*", "")
|
||||||
|
|
||||||
|
// all the needed fields, unneeded fields do not intersect with fromField and output fields
|
||||||
|
f("extract from x '<foo>'", "*", "f1,f2", "*", "f1,f2")
|
||||||
|
|
||||||
|
// all the needed fields, unneeded fields intersect with fromField
|
||||||
|
f("extract from x '<foo>'", "*", "f2,x", "*", "f2")
|
||||||
|
|
||||||
|
// all the needed fields, unneeded fields intersect with output fields
|
||||||
|
f("extract from x '<foo>x<bar>'", "*", "f2,foo", "*", "f2")
|
||||||
|
|
||||||
|
// all the needed fields, unneeded fields intersect with all the output fields
|
||||||
|
f("extract from x '<foo>x<bar>'", "*", "f2,foo,bar", "*", "f2,x")
|
||||||
|
|
||||||
|
// needed fields do not intersect with fromField and output fields
|
||||||
|
f("extract from x '<foo>x<bar>'", "f1,f2", "", "f1,f2", "")
|
||||||
|
|
||||||
|
// needed fields intersect with fromField
|
||||||
|
f("extract from x '<foo>x<bar>'", "f2,x", "", "f2,x", "")
|
||||||
|
|
||||||
|
// needed fields intersect with output fields
|
||||||
|
f("extract from x '<foo>x<bar>'", "f2,foo", "", "f2,x", "")
|
||||||
|
|
||||||
|
// needed fields intersect with fromField and output fields
|
||||||
|
f("extract from x '<foo>x<bar>'", "f2,foo,x,y", "", "f2,x,y", "")
|
||||||
|
}
|
||||||
|
|
|
@ -51,8 +51,8 @@ func BenchmarkExtractFormatApply(b *testing.B) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func benchmarkExtractFormatApply(b *testing.B, format string, a []string) {
|
func benchmarkExtractFormatApply(b *testing.B, pattern string, a []string) {
|
||||||
steps, err := parseExtractFormatSteps(format)
|
steps, err := parseExtractFormatSteps(pattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("unexpected error: %s", err)
|
b.Fatalf("unexpected error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue