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]>
250 lines
4.7 KiB
Go
250 lines
4.7 KiB
Go
package traceql
|
|
|
|
import "fmt"
|
|
|
|
type Operator int
|
|
|
|
const (
|
|
OpNone Operator = iota
|
|
OpAdd
|
|
OpSub
|
|
OpDiv
|
|
OpMod
|
|
OpMult
|
|
OpEqual
|
|
OpNotEqual
|
|
OpRegex
|
|
OpNotRegex
|
|
OpGreater
|
|
OpGreaterEqual
|
|
OpLess
|
|
OpLessEqual
|
|
OpPower
|
|
OpAnd
|
|
OpOr
|
|
OpNot
|
|
OpSpansetChild
|
|
OpSpansetParent
|
|
OpSpansetDescendant
|
|
OpSpansetAncestor
|
|
OpSpansetAnd
|
|
OpSpansetUnion
|
|
OpSpansetSibling
|
|
OpSpansetNotChild
|
|
OpSpansetNotParent
|
|
OpSpansetNotSibling
|
|
OpSpansetNotAncestor
|
|
OpSpansetNotDescendant
|
|
OpSpansetUnionChild
|
|
OpSpansetUnionParent
|
|
OpSpansetUnionSibling
|
|
OpSpansetUnionAncestor
|
|
OpSpansetUnionDescendant
|
|
|
|
// The following operators are used internally and only exist in the AST. They are not parseable in TraceQL
|
|
OpExists
|
|
OpNotExists
|
|
OpIn
|
|
OpNotIn
|
|
OpRegexMatchAny
|
|
OpRegexMatchNone
|
|
)
|
|
|
|
func (op Operator) isBoolean() bool {
|
|
return op == OpOr ||
|
|
op == OpAnd ||
|
|
op == OpEqual ||
|
|
op == OpNotEqual ||
|
|
op == OpRegex ||
|
|
op == OpNotRegex ||
|
|
op == OpGreater ||
|
|
op == OpGreaterEqual ||
|
|
op == OpLess ||
|
|
op == OpLessEqual ||
|
|
op == OpNot ||
|
|
op == OpExists ||
|
|
op == OpNotExists ||
|
|
op == OpIn ||
|
|
op == OpNotIn ||
|
|
op == OpRegexMatchAny ||
|
|
op == OpRegexMatchNone
|
|
}
|
|
|
|
func (op Operator) binaryTypesValid(lhsT StaticType, rhsT StaticType) bool {
|
|
return binaryTypeValid(op, lhsT) && binaryTypeValid(op, rhsT)
|
|
}
|
|
|
|
func binaryTypeValid(op Operator, t StaticType) bool {
|
|
if t == TypeAttribute {
|
|
return true
|
|
}
|
|
|
|
switch t {
|
|
case TypeBoolean, TypeBooleanArray:
|
|
return op == OpAnd ||
|
|
op == OpOr ||
|
|
op == OpEqual ||
|
|
op == OpNotEqual ||
|
|
op == OpIn ||
|
|
op == OpNotIn
|
|
case TypeFloat, TypeFloatArray, TypeInt, TypeIntArray, TypeDuration:
|
|
return op == OpAdd ||
|
|
op == OpSub ||
|
|
op == OpMult ||
|
|
op == OpDiv ||
|
|
op == OpMod ||
|
|
op == OpPower ||
|
|
op == OpEqual ||
|
|
op == OpNotEqual ||
|
|
op == OpGreater ||
|
|
op == OpGreaterEqual ||
|
|
op == OpLess ||
|
|
op == OpLessEqual ||
|
|
op == OpIn ||
|
|
op == OpNotIn
|
|
case TypeString, TypeStringArray:
|
|
return op == OpEqual ||
|
|
op == OpNotEqual ||
|
|
op == OpRegex ||
|
|
op == OpNotRegex ||
|
|
op == OpGreater ||
|
|
op == OpGreaterEqual ||
|
|
op == OpLess ||
|
|
op == OpLessEqual ||
|
|
op == OpIn ||
|
|
op == OpNotIn ||
|
|
op == OpRegexMatchAny ||
|
|
op == OpRegexMatchNone
|
|
case TypeNil, TypeStatus, TypeKind:
|
|
return op == OpEqual || op == OpNotEqual
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (op Operator) unaryTypesValid(t StaticType) bool {
|
|
if t == TypeAttribute {
|
|
return true
|
|
}
|
|
|
|
switch op {
|
|
case OpSub:
|
|
return t.isNumeric()
|
|
case OpNot:
|
|
return t == TypeBoolean
|
|
case OpExists:
|
|
return true
|
|
case OpNotExists:
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// isArrayOp returns true if the operator is a dedicated array operator like IN, NOT IN, MATCH ANY, or MATCH NONE. It
|
|
// returns false for all other operators, even if those operators can operate on arrays like = or !=.
|
|
func (op Operator) isArrayOp() bool {
|
|
return op == OpIn || op == OpNotIn || op == OpRegexMatchAny || op == OpRegexMatchNone
|
|
}
|
|
|
|
// toElementOp returns the equivalent element operator for the given array operator
|
|
func (op Operator) toElementOp() Operator {
|
|
switch op {
|
|
case OpIn:
|
|
return OpEqual
|
|
case OpNotIn:
|
|
return OpNotEqual
|
|
case OpRegexMatchAny:
|
|
return OpRegex
|
|
case OpRegexMatchNone:
|
|
return OpNotRegex
|
|
default:
|
|
return op
|
|
}
|
|
}
|
|
|
|
func (op Operator) String() string {
|
|
switch op {
|
|
case OpAdd:
|
|
return "+"
|
|
case OpSub:
|
|
return "-"
|
|
case OpDiv:
|
|
return "/"
|
|
case OpMod:
|
|
return "%"
|
|
case OpMult:
|
|
return "*"
|
|
case OpEqual:
|
|
return "="
|
|
case OpNotEqual:
|
|
return "!="
|
|
case OpRegex:
|
|
return "=~"
|
|
case OpNotRegex:
|
|
return "!~"
|
|
case OpGreater:
|
|
return ">"
|
|
case OpGreaterEqual:
|
|
return ">="
|
|
case OpLess:
|
|
return "<"
|
|
case OpLessEqual:
|
|
return "<="
|
|
case OpPower:
|
|
return "^"
|
|
case OpAnd:
|
|
return "&&"
|
|
case OpOr:
|
|
return "||"
|
|
case OpNot:
|
|
return "!"
|
|
case OpSpansetChild:
|
|
return ">"
|
|
case OpSpansetParent:
|
|
return "<"
|
|
case OpSpansetDescendant:
|
|
return ">>"
|
|
case OpSpansetAncestor:
|
|
return "<<"
|
|
case OpSpansetAnd:
|
|
return "&&"
|
|
case OpSpansetSibling:
|
|
return "~"
|
|
case OpSpansetUnion:
|
|
return "||"
|
|
case OpSpansetNotChild:
|
|
return "!>"
|
|
case OpSpansetNotParent:
|
|
return "!<"
|
|
case OpSpansetNotSibling:
|
|
return "!~"
|
|
case OpSpansetNotAncestor:
|
|
return "!<<"
|
|
case OpSpansetNotDescendant:
|
|
return "!>>"
|
|
case OpSpansetUnionChild:
|
|
return "&>"
|
|
case OpSpansetUnionParent:
|
|
return "&<"
|
|
case OpSpansetUnionSibling:
|
|
return "&~"
|
|
case OpSpansetUnionAncestor:
|
|
return "&<<"
|
|
case OpSpansetUnionDescendant:
|
|
return "&>>"
|
|
|
|
// Operators IN, NOT IN, MATCH ANY, and MATCH NONE do not exist in the TraceQL syntax (only used internally)
|
|
case OpIn:
|
|
return "IN"
|
|
case OpNotIn:
|
|
return "NOT IN"
|
|
case OpRegexMatchAny:
|
|
return "MATCH ANY"
|
|
case OpRegexMatchNone:
|
|
return "MATCH NONE"
|
|
}
|
|
|
|
return fmt.Sprintf("operator(%d)", op)
|
|
}
|