-
Notifications
You must be signed in to change notification settings - Fork 67
/
bufferfilter.go
120 lines (115 loc) · 3.35 KB
/
bufferfilter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package kernel
import (
"github.com/brimdata/zed"
"github.com/brimdata/zed/compiler/ast/dag"
"github.com/brimdata/zed/runtime/expr"
"github.com/brimdata/zed/zson"
"golang.org/x/text/unicode/norm"
)
// CompileBufferFilter tries to return a BufferFilter for e such that the
// BufferFilter's Eval method returns true for any byte slice containing the ZNG
// encoding of a record matching e. (It may also return true for some byte
// slices that do not match.) compileBufferFilter returns a nil pointer and nil
// error if it cannot construct a useful filter.
func CompileBufferFilter(zctx *zed.Context, e dag.Expr) (*expr.BufferFilter, error) {
switch e := e.(type) {
case *dag.BinaryExpr:
literal, err := isFieldEqualOrIn(zctx, e)
if err != nil {
return nil, err
}
if literal != nil {
return newBufferFilterForLiteral(literal)
}
if e.Op == "and" {
left, err := CompileBufferFilter(zctx, e.LHS)
if err != nil {
return nil, err
}
right, err := CompileBufferFilter(zctx, e.RHS)
if err != nil {
return nil, err
}
if left == nil {
return right, nil
}
if right == nil {
return left, nil
}
return expr.NewAndBufferFilter(left, right), nil
}
if e.Op == "or" {
left, err := CompileBufferFilter(zctx, e.LHS)
if err != nil {
return nil, err
}
right, err := CompileBufferFilter(zctx, e.RHS)
if left == nil || right == nil || err != nil {
return nil, err
}
return expr.NewOrBufferFilter(left, right), nil
}
return nil, nil
case *dag.Search:
literal, err := zson.ParseValue(zctx, e.Value)
if err != nil {
return nil, err
}
switch zed.TypeUnder(literal.Type) {
case zed.TypeNet:
return nil, nil
case zed.TypeString:
pattern := norm.NFC.Bytes(literal.Bytes())
left := expr.NewBufferFilterForStringCase(string(pattern))
if left == nil {
return nil, nil
}
right := expr.NewBufferFilterForFieldName(string(pattern))
return expr.NewOrBufferFilter(left, right), nil
}
left := expr.NewBufferFilterForStringCase(e.Text)
right, err := newBufferFilterForLiteral(literal)
if left == nil || right == nil || err != nil {
return nil, err
}
return expr.NewOrBufferFilter(left, right), nil
default:
return nil, nil
}
}
// XXX isFieldEqualOrIn should work for any paths not just top-level fields.
// See issue #3412
func isFieldEqualOrIn(zctx *zed.Context, e *dag.BinaryExpr) (*zed.Value, error) {
if dag.IsTopLevelField(e.LHS) && e.Op == "==" {
if literal, ok := e.RHS.(*dag.Literal); ok {
val, err := zson.ParseValue(zctx, literal.Value)
if err != nil {
return nil, err
}
return val, nil
}
} else if dag.IsTopLevelField(e.RHS) && e.Op == "in" {
if literal, ok := e.LHS.(*dag.Literal); ok {
val, err := zson.ParseValue(zctx, literal.Value)
if err != nil {
return nil, err
}
if val.Type == zed.TypeNet {
return nil, err
}
return val, nil
}
}
return nil, nil
}
func newBufferFilterForLiteral(val *zed.Value) (*expr.BufferFilter, error) {
if id := val.Type.ID(); zed.IsNumber(id) || id == zed.IDNull {
// All numbers are comparable, so they can require up to three
// patterns: float, varint, and uvarint.
return nil, nil
}
// We're looking for a complete ZNG value, so we can lengthen the
// pattern by calling Encode to add a tag.
pattern := string(val.Encode(nil))
return expr.NewBufferFilterForString(pattern), nil
}