From ae8d774fc8ad95f562c72bbff3da6586eae54168 Mon Sep 17 00:00:00 2001 From: Andrey Karpov Date: Fri, 10 Jan 2025 00:37:29 +0000 Subject: [PATCH] TraceQL: support mixed-type attribute querying (int/float) --- .gitignore | 3 + tempodb/encoding/vparquet2/block_traceql.go | 81 +++++++++++++ .../encoding/vparquet2/block_traceql_test.go | 74 +++++++++++- tempodb/encoding/vparquet3/block_traceql.go | 81 +++++++++++++ .../encoding/vparquet3/block_traceql_test.go | 74 +++++++++++- tempodb/encoding/vparquet4/block_traceql.go | 81 +++++++++++++ .../encoding/vparquet4/block_traceql_test.go | 74 ++++++++++++ tempodb/tempodb_search_test.go | 108 +++++++++++++++++- 8 files changed, 572 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 824e47ac5b6..a730dddcf13 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ integration/e2e/deployments/e2e_integration_test[0-9]* /tmp gh-token.txt .cache +.devcontainer +testdata +k6 diff --git a/tempodb/encoding/vparquet2/block_traceql.go b/tempodb/encoding/vparquet2/block_traceql.go index afbafa11e9d..72044ecd081 100644 --- a/tempodb/encoding/vparquet2/block_traceql.go +++ b/tempodb/encoding/vparquet2/block_traceql.go @@ -1759,6 +1759,71 @@ func createIntPredicate(op traceql.Operator, operands traceql.Operands) (parquet } } +// createIntPredicateFromFloat adapts float-based queries to integer columns. +// If the float operand has no fractional part, it's treated as an integer directly. +// Otherwise, specific shifts are applied (e.g., floor or ceil) depending on the operator, +// or conclude that equality is impossible. +// +// Example: { spanAttr > 3.5 } but 'spanAttr' is stored as int. We'll look for rows where 'spanAttr' >= 4. +func createIntPredicateFromFloat(op traceql.Operator, operands traceql.Operands) (parquetquery.Predicate, error) { + if op == traceql.OpNone { + return nil, nil + } + + f := operands[0].Float() + // Check if f has a fractional part + if _, frac := math.Modf(f); frac == 0 { + // If it's an integer float, treat it purely as int + intOperands := traceql.Operands{traceql.NewStaticInt(int(f))} + return createIntPredicate(op, intOperands) + } + + switch op { + case traceql.OpEqual: + // No integer can be strictly equal to a float with a fractional part + return nil, nil + case traceql.OpNotEqual: + // An integer will always differ from a float that has a fractional part + return parquetquery.NewCallbackPredicate(func() bool { return true }), nil + case traceql.OpGreater, traceql.OpGreaterEqual: + // For > 3.5 or >= 3.5, effectively we do >= 4 + // For > -3.5 or >= -3.5, effectively we do >= -3 + i := int(f) + if i > 0 { + i++ + } + return createIntPredicate(traceql.OpGreaterEqual, traceql.Operands{traceql.NewStaticInt(i)}) + case traceql.OpLess, traceql.OpLessEqual: + // For < 3.5 or <= 3.5, effectively we do <= 3 + // For < -3.5 or <= -3.5, effectively we do <= -4 + i := int(f) + if i < 0 { + i-- + } + return createIntPredicate(traceql.OpLessEqual, traceql.Operands{traceql.NewStaticInt(i)}) + default: + return nil, fmt.Errorf("unsupported operator for float to int conversion: %v", op) + } +} + +// createFloatPredicateFromInt adapts integer-based queries to float columns. +// If the operand can be interpreted as an integer, it's converted to float +// and we delegate further processing to createFloatPredicate. +// +// Example: { spanAttr = 5 } but 'spanAttr' is stored as float. We'll look for rows where 'spanAttr' = 5.0. +func createFloatPredicateFromInt(op traceql.Operator, operands traceql.Operands) (parquetquery.Predicate, error) { + if op == traceql.OpNone { + return nil, nil + } + + if i, ok := operands[0].Int(); ok { + floatOperands := traceql.Operands{traceql.NewStaticFloat(float64(i))} + return createFloatPredicate(op, floatOperands) + } + + return nil, nil +} + func createFloatPredicate(op traceql.Operator, operands traceql.Operands) (parquetquery.Predicate, error) { if op == traceql.OpNone { return nil, nil @@ -1846,13 +1911,29 @@ func createAttributeIterator(makeIter makeIterFn, conditions []traceql.Condition attrStringPreds = append(attrStringPreds, pred) case traceql.TypeInt: + // Create a predicate specifically for integer comparisons pred, err := createIntPredicate(cond.Op, cond.Operands) if err != nil { return nil, fmt.Errorf("creating attribute predicate: %w", err) } attrIntPreds = append(attrIntPreds, pred) + // If the operand can be interpreted as a float, create an additional predicate + if pred, err := createFloatPredicateFromInt(cond.Op, cond.Operands); err != nil { + return nil, fmt.Errorf("creating float attribute predicate from int: %w", err) + } else if pred != nil { + attrFltPreds = append(attrFltPreds, pred) + } + case traceql.TypeFloat: + // Attempt to create a predicate for integer comparisons, if applicable + if pred, err := createIntPredicateFromFloat(cond.Op, cond.Operands); err != nil { + return nil, fmt.Errorf("creating int attribute predicate from float: %w", err) + } else if pred != nil { + attrIntPreds = append(attrIntPreds, pred) + } + + // Create a predicate specifically for float comparisons pred, err := createFloatPredicate(cond.Op, cond.Operands) if err != nil { return nil, fmt.Errorf("creating attribute predicate: %w", err) diff --git a/tempodb/encoding/vparquet2/block_traceql_test.go b/tempodb/encoding/vparquet2/block_traceql_test.go index 92fc146c929..9e148f1b54d 100644 --- a/tempodb/encoding/vparquet2/block_traceql_test.go +++ b/tempodb/encoding/vparquet2/block_traceql_test.go @@ -209,6 +209,39 @@ func TestBackendBlockSearchTraceQL(t *testing.T) { parse(t, `{resource.`+LabelServiceName+` <= 124}`), }, }, + // Cross-type comparisons + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint > 122.9}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint >= 122.9}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint <= 123.0}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint = 123.0}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint != 123.1}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint >= 123.0}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint < 123.1}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint <= 123.1}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint > -123.9}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint >= -123.9}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint <= -123.0}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint = -123.0}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint != -123.1}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint >= -123.0}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint < -122.1}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint <= -122.1}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag > 455}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag >= 455}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag <= 456}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag = 456}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag != 457}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag >= 456}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag <= 457}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag < 457}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag != 455}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag > 455}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag >= 455}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag > 456}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag >= 456}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag <= 457}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag < 457}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag != 457}`), } for _, req := range searchesThatMatch { @@ -316,6 +349,41 @@ func TestBackendBlockSearchTraceQL(t *testing.T) { parse(t, `{`+LabelDuration+` = 100s }`), // Match }, }, + // Cross-type comparisons + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint < 122.9}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint = 122.9}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint <= 122.9}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint < 123.0}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint != 123.0}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint > 123.0}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint >= 123.1}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint = 123.1}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint > 123.1}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint < -123.9}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint = -122.9}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint = -123.9}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint <= -123.9}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint < -123.0}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint != -123.0}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint > -123.0}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint >= -122.1}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint = -122.1}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint > -122.1}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag < 455}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag = 455}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag <= 455}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag < 456}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag != 456}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag > 456}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag >= 457}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag = 457}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag > 457}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag < 456}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag = 456}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag <= 456}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag >= 457}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag = 457}`), + traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag > 457}`), } for _, req := range searchesThatDontMatch { @@ -431,7 +499,11 @@ func fullyPopulatedTestTrace(id common.ID) *Trace { {Key: "bar", ValueInt: intPtr(123)}, {Key: "float", ValueDouble: fltPtr(456.78)}, {Key: "bool", ValueBool: boolPtr(false)}, - + // For cross-type comparisons + {Key: "crossint", ValueInt: intPtr(123)}, + {Key: "crossnint", ValueInt: intPtr(-123)}, + {Key: "crossfloat_nofrag", ValueDouble: fltPtr(456.0)}, + {Key: "crossfloat_frag", ValueDouble: fltPtr(456.78)}, // Edge-cases {Key: LabelName, Value: strPtr("Bob")}, // Conflicts with intrinsic but still looked up by .name {Key: LabelServiceName, Value: strPtr("spanservicename")}, // Overrides resource-level dedicated column diff --git a/tempodb/encoding/vparquet3/block_traceql.go b/tempodb/encoding/vparquet3/block_traceql.go index 209c757c644..3881ce5c8ed 100644 --- a/tempodb/encoding/vparquet3/block_traceql.go +++ b/tempodb/encoding/vparquet3/block_traceql.go @@ -2063,6 +2063,71 @@ func createIntPredicate(op traceql.Operator, operands traceql.Operands) (parquet } } +// createIntPredicateFromFloat adapts float-based queries to integer columns. +// If the float operand has no fractional part, it's treated as an integer directly. +// Otherwise, specific shifts are applied (e.g., floor or ceil) depending on the operator, +// or conclude that equality is impossible. +// +// Example: { spanAttr > 3.5 } but 'spanAttr' is stored as int. We'll look for rows where 'spanAttr' >= 4. +func createIntPredicateFromFloat(op traceql.Operator, operands traceql.Operands) (parquetquery.Predicate, error) { + if op == traceql.OpNone { + return nil, nil + } + + f := operands[0].Float() + // Check if f has a fractional part + if _, frac := math.Modf(f); frac == 0 { + // If it's an integer float, treat it purely as int + intOperands := traceql.Operands{traceql.NewStaticInt(int(f))} + return createIntPredicate(op, intOperands) + } + + switch op { + case traceql.OpEqual: + // No integer can be strictly equal to a float with a fractional part + return nil, nil + case traceql.OpNotEqual: + // An integer will always differ from a float that has a fractional part + return parquetquery.NewCallbackPredicate(func() bool { return true }), nil + case traceql.OpGreater, traceql.OpGreaterEqual: + // For > 3.5 or >= 3.5, effectively we do >= 4 + // For > -3.5 or >= -3.5, effectively we do >= -3 + i := int(f) + if i > 0 { + i++ + } + return createIntPredicate(traceql.OpGreaterEqual, traceql.Operands{traceql.NewStaticInt(i)}) + case traceql.OpLess, traceql.OpLessEqual: + // For < 3.5 or <= 3.5, effectively we do <= 3 + // For < -3.5 or <= -3.5, effectively we do <= -4 + i := int(f) + if i < 0 { + i-- + } + return createIntPredicate(traceql.OpLessEqual, traceql.Operands{traceql.NewStaticInt(i)}) + default: + return nil, fmt.Errorf("unsupported operator for float to int conversion: %v", op) + } +} + +// createFloatPredicateFromInt adapts integer-based queries to float columns. +// If the operand can be interpreted as an integer, it's converted to float +// and we delegate further processing to createFloatPredicate. +// +// Example: { spanAttr = 5 } but 'spanAttr' is stored as float. We'll look for rows where 'spanAttr' = 5.0. +func createFloatPredicateFromInt(op traceql.Operator, operands traceql.Operands) (parquetquery.Predicate, error) { + if op == traceql.OpNone { + return nil, nil + } + + if i, ok := operands[0].Int(); ok { + floatOperands := traceql.Operands{traceql.NewStaticFloat(float64(i))} + return createFloatPredicate(op, floatOperands) + } + + return nil, nil +} + func createFloatPredicate(op traceql.Operator, operands traceql.Operands) (parquetquery.Predicate, error) { if op == traceql.OpNone { return nil, nil @@ -2164,13 +2229,29 @@ func createAttributeIterator(makeIter makeIterFn, conditions []traceql.Condition attrStringPreds = append(attrStringPreds, pred) case traceql.TypeInt: + // Create a predicate specifically for integer comparisons pred, err := createIntPredicate(cond.Op, cond.Operands) if err != nil { return nil, fmt.Errorf("creating attribute predicate: %w", err) } attrIntPreds = append(attrIntPreds, pred) + // If the operand can be interpreted as a float, create an additional predicate + if pred, err := createFloatPredicateFromInt(cond.Op, cond.Operands); err != nil { + return nil, fmt.Errorf("creating float attribute predicate from int: %w", err) + } else if pred != nil { + attrFltPreds = append(attrFltPreds, pred) + } + case traceql.TypeFloat: + // Attempt to create a predicate for integer comparisons, if applicable + if pred, err := createIntPredicateFromFloat(cond.Op, cond.Operands); err != nil { + return nil, fmt.Errorf("creating int attribute predicate from float: %w", err) + } else if pred != nil { + attrIntPreds = append(attrIntPreds, pred) + } + + // Create a predicate specifically for float comparisons pred, err := createFloatPredicate(cond.Op, cond.Operands) if err != nil { return nil, fmt.Errorf("creating attribute predicate: %w", err) diff --git a/tempodb/encoding/vparquet3/block_traceql_test.go b/tempodb/encoding/vparquet3/block_traceql_test.go index 8ab94aff874..c8199b6bdcf 100644 --- a/tempodb/encoding/vparquet3/block_traceql_test.go +++ b/tempodb/encoding/vparquet3/block_traceql_test.go @@ -222,6 +222,39 @@ func TestBackendBlockSearchTraceQL(t *testing.T) { }, }, }, + // Cross-type comparisons + {".crossint > 122.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint > 122.9}`)}, + {".crossint >= 122.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint >= 122.9}`)}, + {".crossint <= 123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint <= 123.0}`)}, + {".crossint = 123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint = 123.0}`)}, + {".crossint != 123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint != 123.1}`)}, + {".crossint >= 123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint >= 123.0}`)}, + {".crossint < 123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint < 123.1}`)}, + {".crossint <= 123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint <= 123.1}`)}, + {".crossnint > -123.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint > -123.9}`)}, + {".crossnint >= -123.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint >= -123.9}`)}, + {".crossnint <= -123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint <= -123.0}`)}, + {".crossnint = -123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint = -123.0}`)}, + {".crossnint != -123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint != -123.1}`)}, + {".crossnint >= -123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint >= -123.0}`)}, + {".crossnint < -122.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint < -122.1}`)}, + {".crossnint <= -122.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint <= -122.1}`)}, + {".crossfloat_nofrag > 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag > 455}`)}, + {".crossfloat_nofrag >= 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag >= 455}`)}, + {".crossfloat_nofrag <= 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag <= 456}`)}, + {".crossfloat_nofrag = 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag = 456}`)}, + {".crossfloat_nofrag != 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag != 457}`)}, + {".crossfloat_nofrag >= 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag >= 456}`)}, + {".crossfloat_nofrag <= 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag <= 457}`)}, + {".crossfloat_nofrag < 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag < 457}`)}, + {".crossfloat_frag != 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag != 455}`)}, + {".crossfloat_frag > 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag > 455}`)}, + {".crossfloat_frag >= 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag >= 455}`)}, + {".crossfloat_frag > 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag > 456}`)}, + {".crossfloat_frag >= 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag >= 456}`)}, + {".crossfloat_frag <= 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag <= 457}`)}, + {".crossfloat_frag < 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag < 457}`)}, + {".crossfloat_frag != 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag != 457}`)}, } for _, tc := range searchesThatMatch { @@ -348,6 +381,41 @@ func TestBackendBlockSearchTraceQL(t *testing.T) { }, }, }, + // Cross-type comparisons + {".crossint < 122.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint < 122.9}`)}, + {".crossint = 122.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint = 122.9}`)}, + {".crossint <= 122.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint <= 122.9}`)}, + {".crossint < 123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint < 123.0}`)}, + {".crossint != 123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint != 123.0}`)}, + {".crossint > 123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint > 123.0}`)}, + {".crossint >= 123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint >= 123.1}`)}, + {".crossint = 123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint = 123.1}`)}, + {".crossint > 123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint > 123.1}`)}, + {".crossnint < -123.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint < -123.9}`)}, + {".crossnint = -122.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint = -122.9}`)}, + {".crossnint = -123.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint = -123.9}`)}, + {".crossnint <= -123.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint <= -123.9}`)}, + {".crossnint < -123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint < -123.0}`)}, + {".crossnint != -123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint != -123.0}`)}, + {".crossnint > -123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint > -123.0}`)}, + {".crossnint >= -122.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint >= -122.1}`)}, + {".crossnint = -122.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint = -122.1}`)}, + {".crossnint > -122.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint > -122.1}`)}, + {".crossfloat_nofrag < 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag < 455}`)}, + {".crossfloat_nofrag = 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag = 455}`)}, + {".crossfloat_nofrag <= 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag <= 455}`)}, + {".crossfloat_nofrag < 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag < 456}`)}, + {".crossfloat_nofrag != 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag != 456}`)}, + {".crossfloat_nofrag > 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag > 456}`)}, + {".crossfloat_nofrag >= 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag >= 457}`)}, + {".crossfloat_nofrag = 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag = 457}`)}, + {".crossfloat_nofrag > 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag > 457}`)}, + {".crossfloat_frag < 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag < 456}`)}, + {".crossfloat_frag = 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag = 456}`)}, + {".crossfloat_frag <= 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag <= 456}`)}, + {".crossfloat_frag >= 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag >= 457}`)}, + {".crossfloat_frag = 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag = 457}`)}, + {".crossfloat_frag > 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag > 457}`)}, } for _, tc := range searchesThatDontMatch { @@ -475,7 +543,11 @@ func fullyPopulatedTestTrace(id common.ID) *Trace { {Key: "bar", ValueInt: intPtr(123)}, {Key: "float", ValueDouble: fltPtr(456.78)}, {Key: "bool", ValueBool: boolPtr(false)}, - + // For cross-type comparisons + {Key: "crossint", ValueInt: intPtr(123)}, + {Key: "crossnint", ValueInt: intPtr(-123)}, + {Key: "crossfloat_nofrag", ValueDouble: fltPtr(456.0)}, + {Key: "crossfloat_frag", ValueDouble: fltPtr(456.78)}, // Edge-cases {Key: LabelName, Value: strPtr("Bob")}, // Conflicts with intrinsic but still looked up by .name {Key: LabelServiceName, Value: strPtr("spanservicename")}, // Overrides resource-level dedicated column diff --git a/tempodb/encoding/vparquet4/block_traceql.go b/tempodb/encoding/vparquet4/block_traceql.go index f67fc272e36..d2dbce80f22 100644 --- a/tempodb/encoding/vparquet4/block_traceql.go +++ b/tempodb/encoding/vparquet4/block_traceql.go @@ -2537,6 +2537,71 @@ func createIntPredicate(op traceql.Operator, operands traceql.Operands) (parquet } } +// createIntPredicateFromFloat adapts float-based queries to integer columns. +// If the float operand has no fractional part, it's treated as an integer directly. +// Otherwise, specific shifts are applied (e.g., floor or ceil) depending on the operator, +// or conclude that equality is impossible. +// +// Example: { spanAttr > 3.5 } but 'spanAttr' is stored as int. We'll look for rows where 'spanAttr' >= 4. +func createIntPredicateFromFloat(op traceql.Operator, operands traceql.Operands) (parquetquery.Predicate, error) { + if op == traceql.OpNone { + return nil, nil + } + + f := operands[0].Float() + // Check if f has a fractional part + if _, frac := math.Modf(f); frac == 0 { + // If it's an integer float, treat it purely as int + intOperands := traceql.Operands{traceql.NewStaticInt(int(f))} + return createIntPredicate(op, intOperands) + } + + switch op { + case traceql.OpEqual: + // No integer can be strictly equal to a float with a fractional part + return nil, nil + case traceql.OpNotEqual: + // An integer will always differ from a float that has a fractional part + return parquetquery.NewCallbackPredicate(func() bool { return true }), nil + case traceql.OpGreater, traceql.OpGreaterEqual: + // For > 3.5 or >= 3.5, effectively we do >= 4 + // For > -3.5 or >= -3.5, effectively we do >= -3 + i := int(f) + if i > 0 { + i++ + } + return createIntPredicate(traceql.OpGreaterEqual, traceql.Operands{traceql.NewStaticInt(i)}) + case traceql.OpLess, traceql.OpLessEqual: + // For < 3.5 or <= 3.5, effectively we do <= 3 + // For < -3.5 or <= -3.5, effectively we do <= -4 + i := int(f) + if i < 0 { + i-- + } + return createIntPredicate(traceql.OpLessEqual, traceql.Operands{traceql.NewStaticInt(i)}) + default: + return nil, fmt.Errorf("unsupported operator for float to int conversion: %v", op) + } +} + +// createFloatPredicateFromInt adapts integer-based queries to float columns. +// If the operand can be interpreted as an integer, it's converted to float +// and we delegate further processing to createFloatPredicate. +// +// Example: { spanAttr = 5 } but 'spanAttr' is stored as float. We'll look for rows where 'spanAttr' = 5.0. +func createFloatPredicateFromInt(op traceql.Operator, operands traceql.Operands) (parquetquery.Predicate, error) { + if op == traceql.OpNone { + return nil, nil + } + + if i, ok := operands[0].Int(); ok { + floatOperands := traceql.Operands{traceql.NewStaticFloat(float64(i))} + return createFloatPredicate(op, floatOperands) + } + + return nil, nil +} + func createFloatPredicate(op traceql.Operator, operands traceql.Operands) (parquetquery.Predicate, error) { if op == traceql.OpNone { return nil, nil @@ -2637,13 +2702,29 @@ func createAttributeIterator(makeIter makeIterFn, conditions []traceql.Condition attrStringPreds = append(attrStringPreds, pred) case traceql.TypeInt: + // Create a predicate specifically for integer comparisons pred, err := createIntPredicate(cond.Op, cond.Operands) if err != nil { return nil, fmt.Errorf("creating attribute predicate: %w", err) } attrIntPreds = append(attrIntPreds, pred) + // If the operand can be interpreted as a float, create an additional predicate + if pred, err := createFloatPredicateFromInt(cond.Op, cond.Operands); err != nil { + return nil, fmt.Errorf("creating float attribute predicate from int: %w", err) + } else if pred != nil { + attrFltPreds = append(attrFltPreds, pred) + } + case traceql.TypeFloat: + // Attempt to create a predicate for integer comparisons, if applicable + if pred, err := createIntPredicateFromFloat(cond.Op, cond.Operands); err != nil { + return nil, fmt.Errorf("creating int attribute predicate from float: %w", err) + } else if pred != nil { + attrIntPreds = append(attrIntPreds, pred) + } + + // Create a predicate specifically for float comparisons pred, err := createFloatPredicate(cond.Op, cond.Operands) if err != nil { return nil, fmt.Errorf("creating attribute predicate: %w", err) diff --git a/tempodb/encoding/vparquet4/block_traceql_test.go b/tempodb/encoding/vparquet4/block_traceql_test.go index b5c0701ebe5..646ed2f0972 100644 --- a/tempodb/encoding/vparquet4/block_traceql_test.go +++ b/tempodb/encoding/vparquet4/block_traceql_test.go @@ -244,6 +244,39 @@ func TestBackendBlockSearchTraceQL(t *testing.T) { }, }, }, + // Cross-type comparisons + {".crossint > 122.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint > 122.9}`)}, + {".crossint >= 122.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint >= 122.9}`)}, + {".crossint <= 123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint <= 123.0}`)}, + {".crossint = 123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint = 123.0}`)}, + {".crossint != 123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint != 123.1}`)}, + {".crossint >= 123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint >= 123.0}`)}, + {".crossint < 123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint < 123.1}`)}, + {".crossint <= 123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint <= 123.1}`)}, + {".crossnint > -123.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint > -123.9}`)}, + {".crossnint >= -123.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint >= -123.9}`)}, + {".crossnint <= -123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint <= -123.0}`)}, + {".crossnint = -123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint = -123.0}`)}, + {".crossnint != -123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint != -123.1}`)}, + {".crossnint >= -123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint >= -123.0}`)}, + {".crossnint < -122.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint < -122.1}`)}, + {".crossnint <= -122.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint <= -122.1}`)}, + {".crossfloat_nofrag > 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag > 455}`)}, + {".crossfloat_nofrag >= 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag >= 455}`)}, + {".crossfloat_nofrag <= 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag <= 456}`)}, + {".crossfloat_nofrag = 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag = 456}`)}, + {".crossfloat_nofrag != 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag != 457}`)}, + {".crossfloat_nofrag >= 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag >= 456}`)}, + {".crossfloat_nofrag <= 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag <= 457}`)}, + {".crossfloat_nofrag < 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag < 457}`)}, + {".crossfloat_frag != 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag != 455}`)}, + {".crossfloat_frag > 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag > 455}`)}, + {".crossfloat_frag >= 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag >= 455}`)}, + {".crossfloat_frag > 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag > 456}`)}, + {".crossfloat_frag >= 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag >= 456}`)}, + {".crossfloat_frag <= 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag <= 457}`)}, + {".crossfloat_frag < 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag < 457}`)}, + {".crossfloat_frag != 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag != 457}`)}, } for _, tc := range searchesThatMatch { @@ -373,6 +406,41 @@ func TestBackendBlockSearchTraceQL(t *testing.T) { }, }, }, + // Cross-type comparisons + {".crossint < 122.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint < 122.9}`)}, + {".crossint = 122.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint = 122.9}`)}, + {".crossint <= 122.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint <= 122.9}`)}, + {".crossint < 123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint < 123.0}`)}, + {".crossint != 123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint != 123.0}`)}, + {".crossint > 123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint > 123.0}`)}, + {".crossint >= 123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint >= 123.1}`)}, + {".crossint = 123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint = 123.1}`)}, + {".crossint > 123.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossint > 123.1}`)}, + {".crossnint < -123.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint < -123.9}`)}, + {".crossnint = -122.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint = -122.9}`)}, + {".crossnint = -123.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint = -123.9}`)}, + {".crossnint <= -123.9", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint <= -123.9}`)}, + {".crossnint < -123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint < -123.0}`)}, + {".crossnint != -123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint != -123.0}`)}, + {".crossnint > -123.0", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint > -123.0}`)}, + {".crossnint >= -122.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint >= -122.1}`)}, + {".crossnint = -122.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint = -122.1}`)}, + {".crossnint > -122.1", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossnint > -122.1}`)}, + {".crossfloat_nofrag < 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag < 455}`)}, + {".crossfloat_nofrag = 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag = 455}`)}, + {".crossfloat_nofrag <= 455", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag <= 455}`)}, + {".crossfloat_nofrag < 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag < 456}`)}, + {".crossfloat_nofrag != 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag != 456}`)}, + {".crossfloat_nofrag > 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag > 456}`)}, + {".crossfloat_nofrag >= 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag >= 457}`)}, + {".crossfloat_nofrag = 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag = 457}`)}, + {".crossfloat_nofrag > 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_nofrag > 457}`)}, + {".crossfloat_frag < 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag < 456}`)}, + {".crossfloat_frag = 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag = 456}`)}, + {".crossfloat_frag <= 456", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag <= 456}`)}, + {".crossfloat_frag >= 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag >= 457}`)}, + {".crossfloat_frag = 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag = 457}`)}, + {".crossfloat_frag > 457", traceql.MustExtractFetchSpansRequestWithMetadata(`{.crossfloat_frag > 457}`)}, } for _, tc := range searchesThatDontMatch { @@ -573,6 +641,12 @@ func fullyPopulatedTestTrace(id common.ID) *Trace { attr("foo", "def"), attr("bar", 123), attr("float", 456.78), + // For cross-type comparisons + attr("crossint", 123), + attr("crossnint", -123), + attr("crossfloat_nofrag", 456.0), + attr("crossfloat_frag", 456.78), + // attr("bool", false), attr("str-array", []string{"value-one", "value-two"}), attr("int-array", []int64{111, 222, 333, 444}), diff --git a/tempodb/tempodb_search_test.go b/tempodb/tempodb_search_test.go index e3f57642621..d12ec21bbf1 100644 --- a/tempodb/tempodb_search_test.go +++ b/tempodb/tempodb_search_test.go @@ -56,6 +56,7 @@ func TestSearchCompleteBlock(t *testing.T) { nestedSet, tagValuesRunner, tagNamesRunner, + traceQLCrossType, ) }) if vers == vparquet4.VersionString { @@ -134,6 +135,100 @@ func traceQLRunner(t *testing.T, _ *tempopb.Trace, wantMeta *tempopb.TraceSearch } } +func traceQLCrossType(t *testing.T, _ *tempopb.Trace, wantMeta *tempopb.TraceSearchMetadata, searchesThatMatch, searchesThatDontMatch []*tempopb.SearchRequest, meta *backend.BlockMeta, r Reader, _ common.BackendBlock) { + ctx := context.Background() + e := traceql.NewEngine() + + quotedAttributesThatMatch := []*tempopb.SearchRequest{ + {Query: `{ .floatAttr > 123.0 }`}, + {Query: `{ .floatAttr >= 123.0 }`}, + {Query: `{ .floatAttr <= 123.4 }`}, + {Query: `{ .floatAttr = 123.4 }`}, + {Query: `{ .floatAttr >= 123.4 }`}, + {Query: `{ .floatAttr <= 123.9 }`}, + {Query: `{ .floatAttr < 123.9 }`}, + {Query: `{ .intAttr > 122 }`}, + {Query: `{ .intAttr >= 122 }`}, + {Query: `{ .intAttr <= 123 }`}, + {Query: `{ .intAttr = 123 }`}, + {Query: `{ .intAttr >= 123 }`}, + {Query: `{ .intAttr <= 124 }`}, + {Query: `{ .intAttr < 124 }`}, + {Query: `{ .floatAttr > 123 }`}, + {Query: `{ .floatAttr >= 123 }`}, + {Query: `{ .floatAttr <= 124 }`}, + {Query: `{ .floatAttr < 124 }`}, + {Query: `{ .intAttr > 122.9 }`}, + {Query: `{ .intAttr >= 122.9 }`}, + {Query: `{ .intAttr <= 123.0 }`}, + {Query: `{ .intAttr = 123.0 }`}, + {Query: `{ .intAttr >= 123.0 }`}, + {Query: `{ .intAttr <= 123.1 }`}, + {Query: `{ .intAttr < 123.1 }`}, + {Query: `{ .intAttr != 123.1 }`}, + } + + searchesThatMatch = append(searchesThatMatch, quotedAttributesThatMatch...) + for _, req := range searchesThatMatch { + fetcher := traceql.NewSpansetFetcherWrapper(func(ctx context.Context, req traceql.FetchSpansRequest) (traceql.FetchSpansResponse, error) { + return r.Fetch(ctx, meta, req, common.DefaultSearchOptions()) + }) + + res, err := e.ExecuteSearch(ctx, req, fetcher) + if errors.Is(err, common.ErrUnsupported) { + continue + } + + require.NoError(t, err, "search request: %+v", req) + actual := actualForExpectedMeta(wantMeta, res) + require.NotNil(t, actual, "search request: %v", req) + actual.SpanSet = nil // todo: add the matching spansets to wantmeta + actual.SpanSets = nil + actual.ServiceStats = nil + require.Equal(t, wantMeta, actual, "search request: %v", req) + } + + quotedAttributesThaDonttMatch := []*tempopb.SearchRequest{ + {Query: `{ .floatAttr < 123.0 }`}, + {Query: `{ .floatAttr <= 123.0 }`}, + {Query: `{ .floatAttr < 123.4 }`}, + {Query: `{ .floatAttr != 123.4 }`}, + {Query: `{ .floatAttr > 123.4 }`}, + {Query: `{ .floatAttr >= 123.9 }`}, + {Query: `{ .floatAttr > 123.9 }`}, + {Query: `{ .intAttr < 122 }`}, + {Query: `{ .intAttr <= 122 }`}, + {Query: `{ .intAttr < 123 }`}, + {Query: `{ .intAttr != 123 }`}, + {Query: `{ .intAttr > 123 }`}, + {Query: `{ .intAttr >= 124 }`}, + {Query: `{ .intAttr > 124 }`}, + {Query: `{ .floatAttr < 123 }`}, + {Query: `{ .floatAttr <= 123 }`}, + {Query: `{ .floatAttr = 123 }`}, + {Query: `{ .floatAttr >= 124 }`}, + {Query: `{ .floatAttr > 124 }`}, + {Query: `{ .intAttr < 122.9 }`}, + {Query: `{ .intAttr <= 122.9 }`}, + {Query: `{ .intAttr < 123.0 }`}, + {Query: `{ .intAttr != 123.0 }`}, + {Query: `{ .intAttr > 123.0 }`}, + {Query: `{ .intAttr >= 123.1 }`}, + {Query: `{ .intAttr > 123.1 }`}, + } + + searchesThatDontMatch = append(searchesThatDontMatch, quotedAttributesThaDonttMatch...) + for _, req := range searchesThatDontMatch { + fetcher := traceql.NewSpansetFetcherWrapper(func(ctx context.Context, req traceql.FetchSpansRequest) (traceql.FetchSpansResponse, error) { + return r.Fetch(ctx, meta, req, common.DefaultSearchOptions()) + }) + + res, err := e.ExecuteSearch(ctx, req, fetcher) + require.NoError(t, err, "search request: %+v", req) + require.Nil(t, actualForExpectedMeta(wantMeta, res), "search request: %v", req) + } +} + func advancedTraceQLRunner(t *testing.T, wantTr *tempopb.Trace, wantMeta *tempopb.TraceSearchMetadata, _, _ []*tempopb.SearchRequest, meta *backend.BlockMeta, r Reader, _ common.BackendBlock) { ctx := context.Background() e := traceql.NewEngine() @@ -1438,7 +1533,7 @@ func tagNamesRunner(t *testing.T, _ *tempopb.Trace, _ *tempopb.TraceSearchMetada query: "{ resource.cluster = `MyCluster` }", expected: map[string][]string{ "span": {"child", "foo", "http.method", "http.status_code", "http.url", "span-dedicated.01", "span-dedicated.02"}, - "resource": {"bat", "{ } ( ) = ~ ! < > & | ^", "cluster", "container", "k8s.cluster.name", "k8s.container.name", "k8s.namespace.name", "k8s.pod.name", "namespace", "pod", "res-dedicated.01", "res-dedicated.02", "service.name"}, + "resource": {"bat", "{ } ( ) = ~ ! < > & | ^", "intAttr", "floatAttr", "cluster", "container", "k8s.cluster.name", "k8s.container.name", "k8s.namespace.name", "k8s.pod.name", "namespace", "pod", "res-dedicated.01", "res-dedicated.02", "service.name"}, }, }, { @@ -1447,7 +1542,7 @@ func tagNamesRunner(t *testing.T, _ *tempopb.Trace, _ *tempopb.TraceSearchMetada query: "{ span.foo = `Bar` }", expected: map[string][]string{ "span": {"child", "parent", "{ } ( ) = ~ ! < > & | ^", "foo", "http.method", "http.status_code", "http.url", "span-dedicated.01", "span-dedicated.02"}, - "resource": {"bat", "{ } ( ) = ~ ! < > & | ^", "cluster", "container", "k8s.cluster.name", "k8s.container.name", "k8s.namespace.name", "k8s.pod.name", "namespace", "pod", "res-dedicated.01", "res-dedicated.02", "service.name"}, + "resource": {"bat", "{ } ( ) = ~ ! < > & | ^", "intAttr", "floatAttr", "cluster", "container", "k8s.cluster.name", "k8s.container.name", "k8s.namespace.name", "k8s.pod.name", "namespace", "pod", "res-dedicated.01", "res-dedicated.02", "service.name"}, }, }, } @@ -1807,6 +1902,13 @@ func intKV(k string, v int) *v1_common.KeyValue { } } +func float64KV(k string, v float64) *v1_common.KeyValue { + return &v1_common.KeyValue{ + Key: k, + Value: &v1_common.AnyValue{Value: &v1_common.AnyValue_DoubleValue{DoubleValue: v}}, + } +} + func boolKV(k string) *v1_common.KeyValue { return &v1_common.KeyValue{ Key: k, @@ -1902,6 +2004,8 @@ func makeExpectedTrace() ( stringKV("res-dedicated.01", "res-1a"), stringKV("res-dedicated.02", "res-2a"), stringKV(attributeWithTerminalChars, "foobar"), + intKV("intAttr", 123), + float64KV("floatAttr", 123.4), }, }, ScopeSpans: []*v1.ScopeSpans{