mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
* Add operators IN, NOT IN, MATCH ANY, and MATCH NONE
* Implement Static.append()
* Print string arrays with '`' instead of '"'
Example ["aa", "bb"] -> [`aa`, `bb`]
* Rewrite binary operations to array equivalent
The rewriter handles the following cases:
- { .a = "a" || .a = "b" } => { .a IN ["a", "b"] }
- { .a != "a" && .a != "b" } => { .a NOT IN ["a", "b"] }
- { .a =~ "a" || .a =~ "b" } => { .a MATCH ANY ["a", "b"] }
- { .a !~ "a" && .a !~ "b" } => { .a MATCH NONE ["a", "b"] }
* Add test cases with arrays to TestBinOp
* traceql.Parse() applies AST optimizations by default
* Rename predicate_test.go to predicates_test.go
* Add array predicates to parquetquery
* BREAKING CHANGE: more precise regex validation
BinaryOperation.validate() considered everythin a valid regex where
FieldExpression.EncodeToString(false) happened to return a valid regex.
With this change the RHS of a regex operation is required to be a string
or string array
* vp4: handle predicates with arrays correctly
* vp5: handle predicates with arrays correctly
* vp3: handle predicates with arrays correctly
* Add test cases for optimized queries
* Update query test examples with optimizable queries
* vp3: fix handling of array types
* CHANGELOG.md
* Pass array operators down to fetch layer
* Read to first virtual row after seeking on a low definition level
This helps avoid additional spans/op in queries with only
resource attributes
---------
Co-authored-by: Martin Disibio <[email protected]>
158 lines
4.3 KiB
Go
158 lines
4.3 KiB
Go
package traceql
|
|
|
|
func (r RootExpr) extractConditions(request *FetchSpansRequest) {
|
|
r.Pipeline.extractConditions(request)
|
|
if r.MetricsPipeline != nil {
|
|
r.MetricsPipeline.extractConditions(request)
|
|
}
|
|
}
|
|
|
|
func (f SpansetFilter) extractConditions(request *FetchSpansRequest) {
|
|
f.Expression.extractConditions(request)
|
|
|
|
// For empty spansets { } ensure there is something that matches all spans.
|
|
// Use start time which would have been selected as part of the second pass
|
|
// metadata, and is still fairly efficient to pull back.
|
|
if s, ok := f.Expression.(Static); ok {
|
|
if b, ok := s.Bool(); !ok || !b {
|
|
return
|
|
}
|
|
|
|
for _, c := range request.Conditions {
|
|
if c.Attribute.Intrinsic != IntrinsicNone && c.Op == OpNone {
|
|
// A different match-all intrinsic is already present.
|
|
return
|
|
}
|
|
}
|
|
|
|
request.appendCondition(Condition{
|
|
Attribute: NewIntrinsic(IntrinsicSpanStartTime),
|
|
Op: OpNone,
|
|
})
|
|
}
|
|
}
|
|
|
|
// extractConditions on Select puts its conditions into the SecondPassConditions
|
|
func (o SelectOperation) extractConditions(request *FetchSpansRequest) {
|
|
selectR := &FetchSpansRequest{}
|
|
for _, expr := range o.attrs {
|
|
expr.extractConditions(selectR)
|
|
}
|
|
// copy any conditions to the normal request's SecondPassConditions
|
|
request.SecondPassConditions = append(request.SecondPassConditions, selectR.Conditions...)
|
|
}
|
|
|
|
func (o *BinaryOperation) extractConditions(request *FetchSpansRequest) {
|
|
// TODO we can further optimise this by attempting to execute every FieldExpression, if they only contain statics it should resolve
|
|
switch l := o.LHS.(type) {
|
|
case Attribute:
|
|
switch r := o.RHS.(type) {
|
|
case Static:
|
|
switch {
|
|
case (o.RHS.(Static).Type == TypeNil && o.Op == OpNotEqual) || !o.Op.isBoolean(): // the fetch layer can't build predicates on operators that are not boolean
|
|
request.appendCondition(Condition{
|
|
Attribute: l,
|
|
Op: OpNone,
|
|
Operands: nil,
|
|
})
|
|
case r.Type == TypeBoolean && (o.Op == OpOr || o.Op == OpAnd):
|
|
request.appendCondition(Condition{
|
|
Attribute: l,
|
|
Op: OpNone,
|
|
Operands: nil,
|
|
})
|
|
default:
|
|
request.appendCondition(Condition{
|
|
Attribute: l,
|
|
Op: o.Op,
|
|
Operands: []Static{r},
|
|
})
|
|
}
|
|
case Attribute:
|
|
// Both sides are attributes, just fetch both
|
|
request.appendCondition(Condition{
|
|
Attribute: o.LHS.(Attribute),
|
|
Op: OpNone,
|
|
Operands: nil,
|
|
})
|
|
request.appendCondition(Condition{
|
|
Attribute: o.RHS.(Attribute),
|
|
Op: OpNone,
|
|
Operands: nil,
|
|
})
|
|
default:
|
|
// Just fetch LHS and try to do something smarter with RHS
|
|
request.appendCondition(Condition{
|
|
Attribute: o.LHS.(Attribute),
|
|
Op: OpNone,
|
|
Operands: nil,
|
|
})
|
|
o.RHS.extractConditions(request)
|
|
}
|
|
case Static:
|
|
switch r := o.RHS.(type) {
|
|
case Static:
|
|
return // if both are Static, don't need to send any conditions
|
|
case Attribute:
|
|
switch {
|
|
case (o.LHS.(Static).Type == TypeNil && o.Op == OpNotEqual) || !o.Op.isBoolean(): // the fetch layer can't build predicates on operators that are not boolean
|
|
request.appendCondition(Condition{
|
|
Attribute: r,
|
|
Op: OpNone,
|
|
Operands: nil,
|
|
})
|
|
case l.Type == TypeBoolean && (o.Op == OpOr || o.Op == OpAnd):
|
|
request.appendCondition(Condition{
|
|
Attribute: r,
|
|
Op: OpNone,
|
|
Operands: nil,
|
|
})
|
|
default:
|
|
request.appendCondition(Condition{
|
|
Attribute: r,
|
|
Op: o.Op,
|
|
Operands: []Static{l},
|
|
})
|
|
}
|
|
default:
|
|
o.RHS.extractConditions(request)
|
|
}
|
|
default:
|
|
o.LHS.extractConditions(request)
|
|
o.RHS.extractConditions(request)
|
|
request.AllConditions = request.AllConditions && (o.Op != OpOr)
|
|
}
|
|
}
|
|
|
|
func (o UnaryOperation) extractConditions(request *FetchSpansRequest) {
|
|
// TODO when Op is Not we should just either negate all inner Operands or just fetch the columns with OpNone
|
|
|
|
switch expr := o.Expression.(type) {
|
|
case Attribute:
|
|
switch o.Op {
|
|
case OpExists, OpNotExists:
|
|
request.appendCondition(Condition{
|
|
Attribute: expr,
|
|
Op: o.Op,
|
|
Operands: nil,
|
|
})
|
|
return
|
|
default:
|
|
expr.extractConditions(request)
|
|
}
|
|
default:
|
|
expr.extractConditions(request)
|
|
}
|
|
}
|
|
|
|
func (s Static) extractConditions(*FetchSpansRequest) {
|
|
}
|
|
|
|
func (a Attribute) extractConditions(request *FetchSpansRequest) {
|
|
request.appendCondition(Condition{
|
|
Attribute: a,
|
|
Op: OpNone,
|
|
Operands: nil,
|
|
})
|
|
}
|