mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-15 12:58:33 +00:00
Logs and traces reads bind the tenant's org
The v5 logs and traces statement builders read event.log and event.span, where every row carries its org. Each read now binds `org = ?` to the tenant: the validated X-Org-Id for a request (set by the Hanzo IAM identity), or the rule's org name for a scheduled rule that reads logs or traces. A read with no tenant is refused as forbidden instead of reading every org. This covers list, time series, scalar and trace requests and both span reads of the trace operator. Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
@@ -62,6 +62,9 @@ func (m *IdentN) Wrap(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
ctx = authtypes.NewContextWithClaims(ctx, claims)
|
||||
if identity.Tenant != "" {
|
||||
ctx = authtypes.NewContextWithTenant(ctx, identity.Tenant)
|
||||
}
|
||||
|
||||
comment := ctxtypes.CommentFromContext(ctx)
|
||||
comment.Set("identn_provider", claims.IdentNProvider.StringValue())
|
||||
|
||||
@@ -146,7 +146,9 @@ func (p *provider) GetIdentity(req *http.Request) (*authtypes.Identity, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return authtypes.NewPrincipalUserIdentity(userID, orgID, email, authtypes.IdentNProviderIAM), nil
|
||||
identity := authtypes.NewPrincipalUserIdentity(userID, orgID, email, authtypes.IdentNProviderIAM)
|
||||
identity.Tenant = orgSlug
|
||||
return identity, nil
|
||||
}
|
||||
|
||||
// provision resolves-or-creates the tenant org AND the person's o11y user row,
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/hanzoai/o11y/pkg/modules/rulestatehistory"
|
||||
"github.com/hanzoai/o11y/pkg/queryparser"
|
||||
"github.com/hanzoai/o11y/pkg/sqlstore"
|
||||
"github.com/hanzoai/o11y/pkg/types/authtypes"
|
||||
qbtypes "github.com/hanzoai/o11y/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/hanzoai/o11y/pkg/types/rulestatehistorytypes"
|
||||
"github.com/hanzoai/o11y/pkg/types/ruletypes"
|
||||
@@ -388,6 +389,43 @@ func (r *BaseRule) SendAlerts(ctx context.Context, ts time.Time, resendDelay tim
|
||||
notifyFunc(ctx, orgID, alerts...)
|
||||
}
|
||||
|
||||
// withTenant scopes ctx to the rule's org when req reads event.log or
|
||||
// event.span, whose builders bind `org = ?`. The tenant is the org's name, the
|
||||
// slug its rows carry in `org`. A metrics-only request needs none.
|
||||
func (r *BaseRule) withTenant(ctx context.Context, req *qbtypes.QueryRangeRequest) (context.Context, error) {
|
||||
if !readsEvents(req) {
|
||||
return ctx, nil
|
||||
}
|
||||
var name string
|
||||
err := r.sqlstore.BunDB().NewSelect().
|
||||
Table("organizations").
|
||||
Column("name").
|
||||
Where("id = ?", r.orgID.StringValue()).
|
||||
Scan(ctx, &name)
|
||||
if err != nil {
|
||||
return ctx, errors.Wrapf(err, errors.TypeNotFound, errors.CodeNotFound, "rule %s: no name for org %s to scope its reads", r.id, r.orgID.StringValue())
|
||||
}
|
||||
return authtypes.NewContextWithTenant(ctx, name), nil
|
||||
}
|
||||
|
||||
// readsEvents reports whether req has a logs or traces builder query, or a
|
||||
// trace operator — the queries that read event.log and event.span.
|
||||
func readsEvents(req *qbtypes.QueryRangeRequest) bool {
|
||||
if req == nil {
|
||||
return false
|
||||
}
|
||||
for _, q := range req.CompositeQuery.Queries {
|
||||
if q.Type == qbtypes.QueryTypeTraceOperator {
|
||||
return true
|
||||
}
|
||||
switch q.Spec.(type) {
|
||||
case qbtypes.QueryBuilderQuery[qbtypes.LogAggregation], qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *BaseRule) ForEachActiveAlert(f func(*ruletypes.Alert)) {
|
||||
r.mtx.Lock()
|
||||
defer r.mtx.Unlock()
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/hanzoai/o11y/pkg/sqlstore"
|
||||
"github.com/hanzoai/o11y/pkg/sqlstore/sqlstoretest"
|
||||
"github.com/hanzoai/o11y/pkg/types/authtypes"
|
||||
qbtypes "github.com/hanzoai/o11y/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/hanzoai/o11y/pkg/types/telemetrytypes"
|
||||
"github.com/hanzoai/o11y/pkg/valuer"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// orgNameStore is a sqlstore that answers the rule's one org-name lookup with
|
||||
// name for orgID.
|
||||
func orgNameStore(t *testing.T, orgID valuer.UUID, name string) sqlstore.SQLStore {
|
||||
t.Helper()
|
||||
store := sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherRegexp)
|
||||
store.Mock().ExpectQuery(`SELECT "name" FROM "organizations" WHERE \(id = '` + orgID.StringValue() + `'\)`).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"name"}).AddRow(name))
|
||||
return store
|
||||
}
|
||||
|
||||
// A rule that reads logs or traces runs with its org's name as the tenant; a
|
||||
// metrics-only rule needs no lookup.
|
||||
func TestRuleReadsCarryTheOrgsTenant(t *testing.T) {
|
||||
orgID := valuer.GenerateUUID()
|
||||
logs := &qbtypes.QueryRangeRequest{CompositeQuery: qbtypes.CompositeQuery{Queries: []qbtypes.QueryEnvelope{{
|
||||
Type: qbtypes.QueryTypeBuilder,
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{Signal: telemetrytypes.SignalLogs},
|
||||
}}}}
|
||||
metrics := &qbtypes.QueryRangeRequest{CompositeQuery: qbtypes.CompositeQuery{Queries: []qbtypes.QueryEnvelope{{
|
||||
Type: qbtypes.QueryTypeBuilder,
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{Signal: telemetrytypes.SignalMetrics},
|
||||
}}}}
|
||||
|
||||
r := &BaseRule{id: "rule-1", orgID: orgID, sqlstore: orgNameStore(t, orgID, "acme")}
|
||||
ctx, err := r.withTenant(context.Background(), logs)
|
||||
require.NoError(t, err)
|
||||
tenant, err := authtypes.TenantFromContext(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "acme", tenant)
|
||||
|
||||
bare := &BaseRule{id: "rule-2", orgID: orgID}
|
||||
ctx, err = bare.withTenant(context.Background(), metrics)
|
||||
require.NoError(t, err)
|
||||
_, err = authtypes.TenantFromContext(ctx)
|
||||
require.Error(t, err)
|
||||
}
|
||||
@@ -178,6 +178,10 @@ func (r *ThresholdRule) buildAndRunQuery(ctx context.Context, orgID valuer.UUID,
|
||||
instrumentationtypes.CodeFunctionName: "buildAndRunQuery",
|
||||
})
|
||||
|
||||
ctx, err = r.withTenant(ctx, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v5Result, err := r.querier.QueryRange(ctx, orgID, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -839,7 +839,7 @@ func TestThresholdRuleTracesLink(t *testing.T) {
|
||||
queryString := "SELECT any"
|
||||
telemetryStore.Mock().
|
||||
ExpectQuery(queryString).
|
||||
WithArgs(nil, nil, nil, nil, nil, nil, nil).
|
||||
WithArgs(nil, nil, nil, nil, nil, nil, nil, nil).
|
||||
WillReturnRows(rows)
|
||||
|
||||
querier := prepareQuerierForTraces(t, telemetryStore, keysMap)
|
||||
@@ -867,7 +867,8 @@ func TestThresholdRuleTracesLink(t *testing.T) {
|
||||
}
|
||||
|
||||
externalURL := mustParseURL(t, "http://localhost:8080")
|
||||
rule, err := NewThresholdRule("69", valuer.GenerateUUID(), &postableRule, querier, logger, externalURL)
|
||||
orgID := valuer.GenerateUUID()
|
||||
rule, err := NewThresholdRule("69", orgID, &postableRule, querier, logger, externalURL, WithSQLStore(orgNameStore(t, orgID, "acme")))
|
||||
if err != nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -957,7 +958,7 @@ func TestThresholdRuleLogsLink(t *testing.T) {
|
||||
queryString := "SELECT any"
|
||||
telemetryStore.Mock().
|
||||
ExpectQuery(queryString).
|
||||
WithArgs(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil).
|
||||
WithArgs(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil).
|
||||
WillReturnRows(rows)
|
||||
|
||||
querier := prepareQuerierForLogs(t, telemetryStore, keysMap)
|
||||
@@ -985,7 +986,8 @@ func TestThresholdRuleLogsLink(t *testing.T) {
|
||||
}
|
||||
|
||||
externalURL := mustParseURL(t, "http://localhost:8080")
|
||||
rule, err := NewThresholdRule("69", valuer.GenerateUUID(), &postableRule, querier, logger, externalURL)
|
||||
orgID := valuer.GenerateUUID()
|
||||
rule, err := NewThresholdRule("69", orgID, &postableRule, querier, logger, externalURL, WithSQLStore(orgNameStore(t, orgID, "acme")))
|
||||
if err != nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package querybuilder
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hanzo-ds/sqlbuilder"
|
||||
"github.com/hanzoai/o11y/pkg/types/authtypes"
|
||||
)
|
||||
|
||||
// TenantCondition is the `org = ?` condition that scopes a read of an event
|
||||
// table (event.log, event.span) to the tenant on ctx. It fails when ctx names no
|
||||
// tenant, so a read that would span every org is refused rather than run.
|
||||
func TenantCondition(ctx context.Context, sb *sqlbuilder.SelectBuilder) (string, error) {
|
||||
tenant, err := authtypes.TenantFromContext(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return sb.E("org", tenant), nil
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package telemetrylogs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -19,7 +18,7 @@ import (
|
||||
|
||||
const (
|
||||
testJSONQueryPrefix = "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE"
|
||||
testJSONQuerySuffix = "AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?"
|
||||
testJSONQuerySuffix = "AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?"
|
||||
)
|
||||
|
||||
type TestExpected struct {
|
||||
@@ -63,8 +62,8 @@ func TestJSONStmtBuilder_TimeSeries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf((dynamicElement(body_v2.`user.age`, 'Int64') IS NOT NULL), toString(dynamicElement(body_v2.`user.age`, 'Int64')), (dynamicElement(body_v2.`user.age`, 'String') IS NOT NULL), dynamicElement(body_v2.`user.age`, 'String'), NULL)) AS `user.age`, count() AS __result_0 FROM event.log WHERE time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? GROUP BY `user.age` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf((dynamicElement(body_v2.`user.age`, 'Int64') IS NOT NULL), toString(dynamicElement(body_v2.`user.age`, 'Int64')), (dynamicElement(body_v2.`user.age`, 'String') IS NOT NULL), dynamicElement(body_v2.`user.age`, 'String'), NULL)) AS `user.age`, count() AS __result_0 FROM event.log WHERE time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND (`user.age`) GLOBAL IN (SELECT `user.age` FROM __limit_cte) GROUP BY ts, `user.age`",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf((dynamicElement(body_v2.`user.age`, 'Int64') IS NOT NULL), toString(dynamicElement(body_v2.`user.age`, 'Int64')), (dynamicElement(body_v2.`user.age`, 'String') IS NOT NULL), dynamicElement(body_v2.`user.age`, 'String'), NULL)) AS `user.age`, count() AS __result_0 FROM event.log WHERE time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? GROUP BY `user.age` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf((dynamicElement(body_v2.`user.age`, 'Int64') IS NOT NULL), toString(dynamicElement(body_v2.`user.age`, 'Int64')), (dynamicElement(body_v2.`user.age`, 'String') IS NOT NULL), dynamicElement(body_v2.`user.age`, 'String'), NULL)) AS `user.age`, count() AS __result_0 FROM event.log WHERE time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? AND (`user.age`) GLOBAL IN (SELECT `user.age` FROM __limit_cte) GROUP BY ts, `user.age`",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -94,7 +93,7 @@ func TestJSONStmtBuilder_TimeSeries(t *testing.T) {
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
|
||||
if c.expectedErrContains != "" {
|
||||
require.Error(t, err)
|
||||
@@ -155,7 +154,7 @@ func TestStmtBuilderTimeSeriesBodyGroupByPromoted(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
if c.expectedErrContains != "" {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), c.expectedErrContains)
|
||||
@@ -184,7 +183,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "message Exists",
|
||||
expected: TestExpected{
|
||||
WhereClause: "body_v2.message <> ?",
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -192,7 +191,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "message Contains 'Iron Award'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "body_v2.message ILIKE ?",
|
||||
Args: []any{"%Iron Award%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%Iron Award%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -202,7 +201,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name = 'x'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((dynamicElement(body_v2.`user.name`, 'String') = ?) AND has(JSONAllPaths(body_v2), 'user.name'))",
|
||||
Args: []any{"x", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"x", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -210,7 +209,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.age Contains 25",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((toString(dynamicElement(body_v2.`user.age`, 'Int64')) ILIKE ?) AND has(JSONAllPaths(body_v2), 'user.age')) OR ((dynamicElement(body_v2.`user.age`, 'String') ILIKE ?) AND has(JSONAllPaths(body_v2), 'user.age')))",
|
||||
Args: []any{"%25%", "%25%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%25%", "%25%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{"Key `user.age` is ambiguous, found 2 different combinations of field context / data type: [name=user.age,context=body,datatype=int64 name=user.age,context=body,datatype=string]."},
|
||||
},
|
||||
},
|
||||
@@ -219,7 +218,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.height Contains 5.8",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((toString(dynamicElement(body_v2.`user.height`, 'Float64')) ILIKE ?) AND has(JSONAllPaths(body_v2), 'user.height'))",
|
||||
Args: []any{"%5.8%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%5.8%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -227,7 +226,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.height > 5.8",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((toFloat64(dynamicElement(body_v2.`user.height`, 'Float64')) > ?) AND has(JSONAllPaths(body_v2), 'user.height'))",
|
||||
Args: []any{5.8, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{5.8, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -235,7 +234,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name LIKE '%ali%'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((dynamicElement(body_v2.`user.name`, 'String') LIKE ?) AND has(JSONAllPaths(body_v2), 'user.name'))",
|
||||
Args: []any{"%ali%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%ali%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -243,7 +242,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name REGEXP 'al.*'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((match(dynamicElement(body_v2.`user.name`, 'String'), ?)) AND has(JSONAllPaths(body_v2), 'user.name'))",
|
||||
Args: []any{"al.*", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"al.*", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -251,7 +250,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.active != true",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(assumeNotNull(dynamicElement(body_v2.`user.active`, 'Bool')) <> ?)",
|
||||
Args: []any{true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -259,7 +258,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.active NOT EXISTS",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(dynamicElement(body_v2.`user.active`, 'Bool') IS NULL)",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -267,7 +266,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name NOT CONTAINS 'IIT'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(assumeNotNull(dynamicElement(body_v2.`user.name`, 'String')) NOT ILIKE ?)",
|
||||
Args: []any{"%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -275,7 +274,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name NOT LIKE '%ali%'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(assumeNotNull(dynamicElement(body_v2.`user.name`, 'String')) NOT LIKE ?)",
|
||||
Args: []any{"%ali%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%ali%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -283,7 +282,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name NOT REGEXP 'al.*'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT match(assumeNotNull(dynamicElement(body_v2.`user.name`, 'String')), ?))",
|
||||
Args: []any{"al.*", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"al.*", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -291,7 +290,7 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.name NOT IN ['alice', 'bob']",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(assumeNotNull(dynamicElement(body_v2.`user.name`, 'String')) NOT IN (?, ?))",
|
||||
Args: []any{"alice", "bob", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"alice", "bob", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -301,14 +300,14 @@ func TestJSONStmtBuilder_PrimitivePaths(t *testing.T) {
|
||||
filter: "body.user.address.zip NOT BETWEEN 100000 AND 999999",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(toFloat64(assumeNotNull(dynamicElement(body_v2.`user.address.zip`, 'Int64'))) NOT BETWEEN ? AND ?)",
|
||||
Args: []any{float64(100000), float64(999999), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(100000), float64(999999), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, qbtypes.RequestTypeRaw,
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, qbtypes.RequestTypeRaw,
|
||||
qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Filter: &qbtypes.Filter{Expression: c.filter},
|
||||
@@ -477,7 +476,7 @@ func TestStatementBuilderListQueryBodyPromoted(t *testing.T) {
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
|
||||
if c.expectedErr != nil {
|
||||
require.Error(t, err)
|
||||
@@ -507,7 +506,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].name Exists",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(arrayExists(`body_v2.education`-> dynamicElement(`body_v2.education`.`name`, 'String') IS NOT NULL, dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -515,7 +514,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].name Contains 'IIT'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> dynamicElement(`body_v2.education`.`name`, 'String') ILIKE ?, dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{"%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -523,7 +522,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].year Contains 2020",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> toString(dynamicElement(`body_v2.education`.`year`, 'Int64')) ILIKE ?, dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{"%2020%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%2020%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -531,7 +530,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].type != '10001'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((NOT arrayExists(`body_v2.education`-> toFloat64OrNull(dynamicElement(`body_v2.education`.`type`, 'String')) = ?, dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND (NOT arrayExists(`body_v2.education`-> dynamicElement(`body_v2.education`.`type`, 'Int64') = ?, dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))))",
|
||||
Args: []any{int64(10001), int64(10001), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{int64(10001), int64(10001), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{"Key `education[].type` is ambiguous, found 2 different combinations of field context / data type: [name=education[].type,context=body,datatype=string name=education[].type,context=body,datatype=int64]."},
|
||||
},
|
||||
},
|
||||
@@ -540,7 +539,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].name NOT EXISTS",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.education`-> dynamicElement(`body_v2.education`.`name`, 'String') IS NOT NULL, dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -548,7 +547,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].name NOT CONTAINS 'IIT'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.education`-> dynamicElement(`body_v2.education`.`name`, 'String') ILIKE ?, dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{"%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%IIT%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -558,7 +557,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].parameters Contains 1.65",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((arrayExists(`body_v2.education`-> (arrayExists(x -> toString(x) ILIKE ?, dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))')) OR arrayExists(x -> toFloat64(x) = ?, dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))'))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')) OR ((arrayExists(`body_v2.education`-> (arrayExists(x -> toString(x) ILIKE ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)'))) OR arrayExists(x -> accurateCastOrNull(x, 'Float64') = ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)')))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')))",
|
||||
Args: []any{"%1.65%", 1.65, "%1.65%", 1.65, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%1.65%", 1.65, "%1.65%", 1.65, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64 name=education[].parameters,context=body,datatype=[]dynamic]."},
|
||||
},
|
||||
},
|
||||
@@ -567,7 +566,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].parameters = 'passed'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((arrayExists(`body_v2.education`-> arrayExists(x -> toString(x) = ?, dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))')), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')) OR ((arrayExists(`body_v2.education`-> arrayExists(x -> toString(x) = ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)'))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')))",
|
||||
Args: []any{"passed", "passed", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"passed", "passed", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64 name=education[].parameters,context=body,datatype=[]dynamic]."},
|
||||
},
|
||||
},
|
||||
@@ -576,7 +575,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].parameters Contains 'passed'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((arrayExists(`body_v2.education`-> (arrayExists(x -> toString(x) ILIKE ?, dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))')) OR arrayExists(x -> toString(x) = ?, dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))'))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')) OR ((arrayExists(`body_v2.education`-> (arrayExists(x -> toString(x) ILIKE ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)'))) OR arrayExists(x -> toString(x) = ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)')))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')))",
|
||||
Args: []any{"%passed%", "passed", "%passed%", "passed", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%passed%", "passed", "%passed%", "passed", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64 name=education[].parameters,context=body,datatype=[]dynamic]."},
|
||||
},
|
||||
},
|
||||
@@ -585,7 +584,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].parameters IN [1.65, 1.99]",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((arrayExists(`body_v2.education`-> arrayExists(x -> toFloat64(x) IN (?, ?), dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))')), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')) OR ((arrayExists(`body_v2.education`-> arrayExists(x -> toString(x) IN (?, ?), arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)'))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education')))",
|
||||
Args: []any{1.65, 1.99, "1.65", "1.99", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{1.65, 1.99, "1.65", "1.99", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64 name=education[].parameters,context=body,datatype=[]dynamic]."},
|
||||
},
|
||||
},
|
||||
@@ -594,7 +593,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].parameters NOT CONTAINS 1.65",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((NOT arrayExists(`body_v2.education`-> (arrayExists(x -> toString(x) ILIKE ?, dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))')) OR arrayExists(x -> toFloat64(x) = ?, dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))'))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND (NOT arrayExists(`body_v2.education`-> (arrayExists(x -> toString(x) ILIKE ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)'))) OR arrayExists(x -> accurateCastOrNull(x, 'Float64') = ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)')))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))))",
|
||||
Args: []any{"%1.65%", float64(1.65), "%1.65%", float64(1.65), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%1.65%", float64(1.65), "%1.65%", float64(1.65), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64 name=education[].parameters,context=body,datatype=[]dynamic]."},
|
||||
},
|
||||
},
|
||||
@@ -603,7 +602,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "has(body.education[].parameters, 1.65)",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(has(arrayFlatten(arrayConcat(arrayMap(`body_v2.education`->dynamicElement(`body_v2.education`.`parameters`, 'Array(Nullable(Float64))'), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))), ?) OR has(arrayFlatten(arrayConcat(arrayMap(`body_v2.education`->dynamicElement(`body_v2.education`.`parameters`, 'Array(Dynamic)'), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))), ?))",
|
||||
Args: []any{1.65, 1.65, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{1.65, 1.65, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{
|
||||
"Key `education[].parameters` is ambiguous, found 2 different combinations of field context / data type: [name=education[].parameters,context=body,datatype=[]float64 name=education[].parameters,context=body,datatype=[]dynamic].",
|
||||
},
|
||||
@@ -614,7 +613,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "hasAll(body.user.permissions, ['read', 'write'])",
|
||||
expected: TestExpected{
|
||||
WhereClause: "hasAll(dynamicElement(body_v2.`user.permissions`, 'Array(Nullable(String))'), ?)",
|
||||
Args: []any{[]any{"read", "write"}, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{[]any{"read", "write"}, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -624,7 +623,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.http-events[].request-info.host != 'example.com'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.http-events`-> dynamicElement(`body_v2.http-events`.`request-info.host`, 'String') = ?, dynamicElement(body_v2.`http-events`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{"example.com", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"example.com", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -634,7 +633,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].awards[].name Exists",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`name`, 'String') IS NOT NULL, dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`name`, 'String') IS NOT NULL, arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -642,7 +641,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].awards[].name = 'Iron Award'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`name`, 'String') = ?, dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`name`, 'String') = ?, arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{"Iron Award", "Iron Award", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"Iron Award", "Iron Award", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -650,7 +649,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "education[].awards[].semester BETWEEN 2 AND 4",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> toFloat64(dynamicElement(`body_v2.education[].awards`.`semester`, 'Int64')) BETWEEN ? AND ?, dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> toFloat64(dynamicElement(`body_v2.education[].awards`.`semester`, 'Int64')) BETWEEN ? AND ?, arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{float64(2), float64(4), float64(2), float64(4), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(2), float64(4), float64(2), float64(4), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -658,7 +657,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "education[].awards[].semester IN [2, 4]",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> toFloat64(dynamicElement(`body_v2.education[].awards`.`semester`, 'Int64')) IN (?, ?), dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> toFloat64(dynamicElement(`body_v2.education[].awards`.`semester`, 'Int64')) IN (?, ?), arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{float64(2), float64(4), float64(2), float64(4), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(2), float64(4), float64(2), float64(4), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -666,7 +665,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].awards[].type != 'sports'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`type`, 'String') = ?, dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`type`, 'String') = ?, arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{"sports", "sports", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"sports", "sports", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -674,7 +673,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].awards[].name NOT EXISTS",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`name`, 'String') IS NOT NULL, dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> dynamicElement(`body_v2.education[].awards`.`name`, 'String') IS NOT NULL, arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -684,7 +683,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.interests[].entities[].reviews[].entries[].metadata[].positions[].ratings Contains 4",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((arrayExists(`body_v2.interests`-> arrayExists(`body_v2.interests[].entities`-> arrayExists(`body_v2.interests[].entities[].reviews`-> arrayExists(`body_v2.interests[].entities[].reviews[].entries`-> arrayExists(`body_v2.interests[].entities[].reviews[].entries[].metadata`-> arrayExists(`body_v2.interests[].entities[].reviews[].entries[].metadata[].positions`-> (arrayExists(x -> toString(x) ILIKE ?, dynamicElement(`body_v2.interests[].entities[].reviews[].entries[].metadata[].positions`.`ratings`, 'Array(Nullable(Int64))')) OR arrayExists(x -> toFloat64(x) = ?, dynamicElement(`body_v2.interests[].entities[].reviews[].entries[].metadata[].positions`.`ratings`, 'Array(Nullable(Int64))'))), dynamicElement(`body_v2.interests[].entities[].reviews[].entries[].metadata`.`positions`, 'Array(JSON(max_dynamic_types=0, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests[].entities[].reviews[].entries`.`metadata`, 'Array(JSON(max_dynamic_types=1, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests[].entities[].reviews`.`entries`, 'Array(JSON(max_dynamic_types=2, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests[].entities`.`reviews`, 'Array(JSON(max_dynamic_types=4, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests`.`entities`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), dynamicElement(body_v2.`interests`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'interests')) OR ((arrayExists(`body_v2.interests`-> arrayExists(`body_v2.interests[].entities`-> arrayExists(`body_v2.interests[].entities[].reviews`-> arrayExists(`body_v2.interests[].entities[].reviews[].entries`-> arrayExists(`body_v2.interests[].entities[].reviews[].entries[].metadata`-> arrayExists(`body_v2.interests[].entities[].reviews[].entries[].metadata[].positions`-> (arrayExists(x -> x ILIKE ?, dynamicElement(`body_v2.interests[].entities[].reviews[].entries[].metadata[].positions`.`ratings`, 'Array(Nullable(String))')) OR arrayExists(x -> toFloat64OrNull(x) = ?, dynamicElement(`body_v2.interests[].entities[].reviews[].entries[].metadata[].positions`.`ratings`, 'Array(Nullable(String))'))), dynamicElement(`body_v2.interests[].entities[].reviews[].entries[].metadata`.`positions`, 'Array(JSON(max_dynamic_types=0, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests[].entities[].reviews[].entries`.`metadata`, 'Array(JSON(max_dynamic_types=1, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests[].entities[].reviews`.`entries`, 'Array(JSON(max_dynamic_types=2, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests[].entities`.`reviews`, 'Array(JSON(max_dynamic_types=4, max_dynamic_paths=0))')), dynamicElement(`body_v2.interests`.`entities`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), dynamicElement(body_v2.`interests`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'interests')))",
|
||||
Args: []any{"%4%", float64(4), "%4%", float64(4), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%4%", float64(4), "%4%", float64(4), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{"Key `interests[].entities[].reviews[].entries[].metadata[].positions[].ratings` is ambiguous, found 2 different combinations of field context / data type: [name=interests[].entities[].reviews[].entries[].metadata[].positions[].ratings,context=body,datatype=[]int64 name=interests[].entities[].reviews[].entries[].metadata[].positions[].ratings,context=body,datatype=[]string]."},
|
||||
},
|
||||
},
|
||||
@@ -693,7 +692,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].awards[].participated[].team[].branch Contains 'Civil'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> (arrayExists(`body_v2.education[].awards`-> (arrayExists(`body_v2.education[].awards[].participated`-> arrayExists(`body_v2.education[].awards[].participated[].team`-> dynamicElement(`body_v2.education[].awards[].participated[].team`.`branch`, 'String') ILIKE ?, dynamicElement(`body_v2.education[].awards[].participated`.`team`, 'Array(JSON(max_dynamic_types=2, max_dynamic_paths=0))')), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=4, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards[].participated`-> arrayExists(`body_v2.education[].awards[].participated[].team`-> dynamicElement(`body_v2.education[].awards[].participated[].team`.`branch`, 'String') ILIKE ?, dynamicElement(`body_v2.education[].awards[].participated`.`team`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))')), arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')) OR arrayExists(`body_v2.education[].awards`-> (arrayExists(`body_v2.education[].awards[].participated`-> arrayExists(`body_v2.education[].awards[].participated[].team`-> dynamicElement(`body_v2.education[].awards[].participated[].team`.`branch`, 'String') ILIKE ?, dynamicElement(`body_v2.education[].awards[].participated`.`team`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=64))')), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))')) OR arrayExists(`body_v2.education[].awards[].participated`-> arrayExists(`body_v2.education[].awards[].participated[].team`-> dynamicElement(`body_v2.education[].awards[].participated[].team`.`branch`, 'String') ILIKE ?, dynamicElement(`body_v2.education[].awards[].participated`.`team`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))')), arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), arrayMap(x->dynamicElement(x, 'JSON'), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{"%Civil%", "%Civil%", "%Civil%", "%Civil%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%Civil%", "%Civil%", "%Civil%", "%Civil%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -702,7 +701,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].scores IN [90, 95]",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> arrayExists(x -> toFloat64(x) IN (?, ?), dynamicElement(`body_v2.education`.`scores`, 'Array(Nullable(Int64))')), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{float64(90), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(90), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -712,7 +711,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].scores NOT IN [90, 95]",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.education`-> arrayExists(x -> toFloat64(x) IN (?, ?), dynamicElement(`body_v2.education`.`scores`, 'Array(Nullable(Int64))')), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{float64(90), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(90), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -722,7 +721,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].scores BETWEEN 80 AND 95",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.education`-> arrayExists(x -> toFloat64(x) BETWEEN ? AND ?, dynamicElement(`body_v2.education`.`scores`, 'Array(Nullable(Int64))')), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'education'))",
|
||||
Args: []any{float64(80), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(80), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -732,7 +731,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "body.education[].scores NOT BETWEEN 80 AND 95",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.education`-> arrayExists(x -> toFloat64(x) BETWEEN ? AND ?, dynamicElement(`body_v2.education`.`scores`, 'Array(Nullable(Int64))')), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{float64(80), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(80), float64(95), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -740,7 +739,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "hasAny(education[].awards[].participated[].members, ['Piyush', 'Tushar'])",
|
||||
expected: TestExpected{
|
||||
WhereClause: "hasAny(arrayFlatten(arrayConcat(arrayMap(`body_v2.education`->arrayConcat(arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=4, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))), ?)",
|
||||
Args: []any{[]any{"Piyush", "Tushar"}, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{[]any{"Piyush", "Tushar"}, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -748,7 +747,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "interests[].entities[].product_codes Contains 1",
|
||||
expected: TestExpected{
|
||||
WhereClause: "((arrayExists(`body_v2.interests`-> arrayExists(`body_v2.interests[].entities`-> (arrayExists(x -> toString(x) ILIKE ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.interests[].entities`.`product_codes`, 'Array(Dynamic)'))) OR arrayExists(x -> accurateCastOrNull(x, 'Float64') = ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.interests[].entities`.`product_codes`, 'Array(Dynamic)')))), dynamicElement(`body_v2.interests`.`entities`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), dynamicElement(body_v2.`interests`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))'))) AND has(JSONAllPaths(body_v2), 'interests'))",
|
||||
Args: []any{"%1%", float64(1), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"%1%", float64(1), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -756,7 +755,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "has(interests[].entities[].product_codes, '2002')",
|
||||
expected: TestExpected{
|
||||
WhereClause: "has(arrayFlatten(arrayConcat(arrayMap(`body_v2.interests`->arrayMap(`body_v2.interests[].entities`->dynamicElement(`body_v2.interests[].entities`.`product_codes`, 'Array(Dynamic)'), dynamicElement(`body_v2.interests`.`entities`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), dynamicElement(body_v2.`interests`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))), ?)",
|
||||
Args: []any{"2002", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"2002", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -764,7 +763,7 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "interests[].entities[].product_codes != '1'",
|
||||
expected: TestExpected{
|
||||
WhereClause: "(NOT arrayExists(`body_v2.interests`-> arrayExists(`body_v2.interests[].entities`-> arrayExists(x -> accurateCastOrNull(x, 'Float64') = ?, arrayFilter(x->(dynamicType(x) IN ('String', 'Int64', 'Float64', 'Bool')), dynamicElement(`body_v2.interests[].entities`.`product_codes`, 'Array(Dynamic)'))), dynamicElement(`body_v2.interests`.`entities`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), dynamicElement(body_v2.`interests`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))",
|
||||
Args: []any{int64(1), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{int64(1), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -772,14 +771,14 @@ func TestJSONStmtBuilder_ArrayPaths(t *testing.T) {
|
||||
filter: "has(interests[].entities[].product_codes, 1001)",
|
||||
expected: TestExpected{
|
||||
WhereClause: "has(arrayFlatten(arrayConcat(arrayMap(`body_v2.interests`->arrayMap(`body_v2.interests[].entities`->dynamicElement(`body_v2.interests[].entities`.`product_codes`, 'Array(Dynamic)'), dynamicElement(`body_v2.interests`.`entities`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), dynamicElement(body_v2.`interests`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))), ?)",
|
||||
Args: []any{float64(1001), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(1001), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, qbtypes.RequestTypeRaw,
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, qbtypes.RequestTypeRaw,
|
||||
qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
Filter: &qbtypes.Filter{Expression: c.filter},
|
||||
@@ -816,7 +815,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "((lower(assumeNotNull(dynamicElement(body_v2.user.name, 'String'))) = ?) AND has(JSONAllPaths(body_v2), 'user.name'))",
|
||||
Args: []any{"alice", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"alice", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -828,7 +827,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "((toFloat64(assumeNotNull(dynamicElement(body_v2.user.address.zip, 'Int64'))) = ?) AND has(JSONAllPaths(body_v2), 'user.address.zip'))",
|
||||
Args: []any{float64(110001), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(110001), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
// ── indexed exists: index is skipped; emits plain IS NOT NULL ───────────
|
||||
@@ -844,7 +843,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "(dynamicElement(body_v2.`user.name`, 'String') IS NOT NULL)",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
// ── indexed not-equal: assumeNotNull wrapping + no path index ─────────
|
||||
@@ -857,7 +856,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "(lower(assumeNotNull(dynamicElement(body_v2.user.name, 'String'))) <> ?)",
|
||||
Args: []any{"alice", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{"alice", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -871,7 +870,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "(((toFloat64(assumeNotNull(dynamicElement(body_v2.`http-status`, 'Int64'))) = ?) AND has(JSONAllPaths(body_v2), 'http-status')) OR ((toFloat64OrNull(lower(assumeNotNull(dynamicElement(body_v2.`http-status`, 'String')))) = ?) AND has(JSONAllPaths(body_v2), 'http-status')))",
|
||||
Args: []any{float64(200), float64(200), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(200), float64(200), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -883,7 +882,7 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "((toFloat64(assumeNotNull(dynamicElement(body_v2.response.`time-taken`, 'Float64'))) > ?) AND has(JSONAllPaths(body_v2), 'response.time-taken'))",
|
||||
Args: []any{float64(1.5), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{float64(1.5), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
// ── Bool is NOT IndexSupported — index branch must be skipped ─────────
|
||||
@@ -897,14 +896,14 @@ func TestJSONStmtBuilder_IndexedPaths(t *testing.T) {
|
||||
},
|
||||
expected: TestExpected{
|
||||
WhereClause: "((dynamicElement(body_v2.`user.active`, 'Bool') = ?) AND has(JSONAllPaths(body_v2), 'user.active'))",
|
||||
Args: []any{true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Args: []any{true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, qbtypes.RequestTypeRaw, c.query, nil)
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, qbtypes.RequestTypeRaw, c.query, nil)
|
||||
if c.expectedErr != nil {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), c.expectedErr.Error())
|
||||
@@ -944,8 +943,8 @@ func TestJSONStmtBuilder_SelectField(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, arrayFlatten(arrayConcat(arrayMap(`body_v2.education`->arrayConcat(arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=4, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))) AS `education[].awards[].participated[].members` FROM event.log WHERE (dynamicElement(body_v2.`user.name`, 'String') IS NOT NULL) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, arrayFlatten(arrayConcat(arrayMap(`body_v2.education`->arrayConcat(arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=4, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))) AS `education[].awards[].participated[].members` FROM event.log WHERE (dynamicElement(body_v2.`user.name`, 'String') IS NOT NULL) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -965,8 +964,8 @@ func TestJSONStmtBuilder_SelectField(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, arrayFlatten(arrayConcat(arrayMap(`body_v2.education`->arrayConcat(arrayMap(`body_v2.education[].awards`->dynamicElement(`body_v2.education[].awards`.`type`, 'String'), dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards`->dynamicElement(`body_v2.education[].awards`.`type`, 'String'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))) AS `education[].awards[].type` FROM event.log WHERE (dynamicElement(body_v2.`user.name`, 'String') IS NOT NULL) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, arrayFlatten(arrayConcat(arrayMap(`body_v2.education`->arrayConcat(arrayMap(`body_v2.education[].awards`->dynamicElement(`body_v2.education[].awards`.`type`, 'String'), dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards`->dynamicElement(`body_v2.education[].awards`.`type`, 'String'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))) AS `education[].awards[].type` FROM event.log WHERE (dynamicElement(body_v2.`user.name`, 'String') IS NOT NULL) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -983,15 +982,15 @@ func TestJSONStmtBuilder_SelectField(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, dynamicElement(body_v2.`user.name`, 'String') AS `user.name` FROM event.log WHERE time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, dynamicElement(body_v2.`user.name`, 'String') AS `user.name` FROM event.log WHERE time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
if c.expectedErrContains != "" {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), c.expectedErrContains)
|
||||
@@ -1037,8 +1036,8 @@ func TestJSONStmtBuilder_OrderBy(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE (dynamicElement(body_v2.`user.name`, 'String') IS NOT NULL) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? ORDER BY arrayFlatten(arrayConcat(arrayMap(`body_v2.education`->arrayConcat(arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=4, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))) AS `education[].awards[].participated[].members` asc LIMIT ?",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE (dynamicElement(body_v2.`user.name`, 'String') IS NOT NULL) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? ORDER BY arrayFlatten(arrayConcat(arrayMap(`body_v2.education`->arrayConcat(arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=4, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), dynamicElement(`body_v2.education`.`awards`, 'Array(JSON(max_dynamic_types=8, max_dynamic_paths=0))')), arrayMap(`body_v2.education[].awards`->arrayConcat(arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))')), arrayMap(`body_v2.education[].awards[].participated`->dynamicElement(`body_v2.education[].awards[].participated`.`members`, 'Array(Nullable(String))'), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education[].awards`.`participated`, 'Array(Dynamic)'))))), arrayMap(x->assumeNotNull(dynamicElement(x, 'JSON')), arrayFilter(x->(dynamicType(x) = 'JSON'), dynamicElement(`body_v2.education`.`awards`, 'Array(Dynamic)'))))), dynamicElement(body_v2.`education`, 'Array(JSON(max_dynamic_types=16, max_dynamic_paths=0))')))) AS `education[].awards[].participated[].members` asc LIMIT ?",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1060,15 +1059,15 @@ func TestJSONStmtBuilder_OrderBy(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? ORDER BY dynamicElement(body_v2.`user.name`, 'String') AS `user.name` asc LIMIT ?",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? ORDER BY dynamicElement(body_v2.`user.name`, 'String') AS `user.name` asc LIMIT ?",
|
||||
Args: []any{"1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
if c.expectedErrContains != "" {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), c.expectedErrContains)
|
||||
@@ -1122,8 +1121,8 @@ func TestResourceAggrAndGroupBy_WithJSONEnabled(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(mapContains(attributes, 'region') = ?, attributes['region'], NULL)) AS `region`, countDistinct(multiIf(notEmpty(service), service, NULL)) AS __result_0 FROM event.log WHERE ((dynamicElement(body_v2.`user.name`, 'String') IS NOT NULL) OR mapContains(attributes, 'user.name') = ?) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? GROUP BY ts, `region`",
|
||||
Args: []any{true, true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448)},
|
||||
Query: "SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(mapContains(attributes, 'region') = ?, attributes['region'], NULL)) AS `region`, countDistinct(multiIf(notEmpty(service), service, NULL)) AS __result_0 FROM event.log WHERE ((dynamicElement(body_v2.`user.name`, 'String') IS NOT NULL) OR mapContains(attributes, 'user.name') = ?) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? GROUP BY ts, `region`",
|
||||
Args: []any{true, true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant},
|
||||
Warnings: []string{"Key `user.name` is ambiguous, found 2 different combinations of field context / data type: [name=user.name,context=body,datatype=string name=user.name,context=attribute,datatype=string]."},
|
||||
},
|
||||
},
|
||||
@@ -1131,7 +1130,7 @@ func TestResourceAggrAndGroupBy_WithJSONEnabled(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
if c.expectedErrContains != "" {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), c.expectedErrContains)
|
||||
|
||||
@@ -89,7 +89,7 @@ func TestLiveProofLogs(t *testing.T) {
|
||||
Order: []qbtypes.OrderBy{{Key: qbtypes.OrderByKey{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "time"}}, Direction: qbtypes.OrderDirectionDesc}},
|
||||
Limit: 10,
|
||||
}
|
||||
stmt, err := statementBuilder.Build(context.Background(), start, end, qbtypes.RequestTypeRaw, q, nil)
|
||||
stmt, err := statementBuilder.Build(tenantCtx(), start, end, qbtypes.RequestTypeRaw, q, nil)
|
||||
require.NoError(t, err)
|
||||
n := execLogCount(t, ts, *stmt)
|
||||
t.Logf("log search rows=%d\nSQL: %s\nARGS: %v", n, stmt.Query, stmt.Args)
|
||||
@@ -105,7 +105,7 @@ func TestLiveProofLogs(t *testing.T) {
|
||||
GroupBy: []qbtypes.GroupByKey{{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "severity_text"}}},
|
||||
Limit: 10,
|
||||
}
|
||||
stmt, err := statementBuilder.Build(context.Background(), start, end, qbtypes.RequestTypeTimeSeries, q, nil)
|
||||
stmt, err := statementBuilder.Build(tenantCtx(), start, end, qbtypes.RequestTypeTimeSeries, q, nil)
|
||||
require.NoError(t, err)
|
||||
n := execLogCount(t, ts, *stmt)
|
||||
t.Logf("log timeseries rows=%d\nSQL: %s\nARGS: %v", n, stmt.Query, stmt.Args)
|
||||
@@ -118,7 +118,7 @@ func TestLiveProofLogs(t *testing.T) {
|
||||
Filter: &qbtypes.Filter{Expression: "trace_id EXISTS"},
|
||||
Limit: 10,
|
||||
}
|
||||
stmt, err := statementBuilder.Build(context.Background(), start, end, qbtypes.RequestTypeRaw, q, nil)
|
||||
stmt, err := statementBuilder.Build(tenantCtx(), start, end, qbtypes.RequestTypeRaw, q, nil)
|
||||
require.NoError(t, err)
|
||||
n := execLogCount(t, ts, *stmt)
|
||||
t.Logf("log trace_id exists rows=%d\nSQL: %s\nARGS: %v", n, stmt.Query, stmt.Args)
|
||||
|
||||
@@ -712,6 +712,12 @@ func (b *logQueryStatementBuilder) addFilterCondition(
|
||||
sb.Where(sb.L("time", fmt.Sprintf("%d", end)), sb.LE("ts_bucket_start", endBucket))
|
||||
}
|
||||
|
||||
tenant, err := querybuilder.TenantCondition(ctx, sb)
|
||||
if err != nil {
|
||||
return preparedWhereClause, err
|
||||
}
|
||||
sb.Where(tenant)
|
||||
|
||||
return preparedWhereClause, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package telemetrylogs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -72,8 +71,8 @@ func TestStatementBuilderTimeSeries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, countDistinct(multiIf(notEmpty(service), service, NULL)) AS __result_0 FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, countDistinct(multiIf(notEmpty(service), service, NULL)) AS __result_0 FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1705397400), uint64(1705485600), "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600), 10, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600)},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, countDistinct(multiIf(notEmpty(service), service, NULL)) AS __result_0 FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, countDistinct(multiIf(notEmpty(service), service, NULL)) AS __result_0 FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1705397400), uint64(1705485600), "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600), testTenant, 10, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -103,8 +102,8 @@ func TestStatementBuilderTimeSeries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, countDistinct(multiIf(notEmpty(service), service, NULL)) AS __result_0 FROM event.log WHERE (service = ? OR (attributes['http.method'] = ? AND mapContains(attributes, 'http.method') = ?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, countDistinct(multiIf(notEmpty(service), service, NULL)) AS __result_0 FROM event.log WHERE (service = ? OR (attributes['http.method'] = ? AND mapContains(attributes, 'http.method') = ?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "GET", true, "1705226400000000000", uint64(1705224600), "1705485600000000000", uint64(1705485600), 10, "redis-manual", "GET", true, "1705226400000000000", uint64(1705224600), "1705485600000000000", uint64(1705485600)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, countDistinct(multiIf(notEmpty(service), service, NULL)) AS __result_0 FROM event.log WHERE (service = ? OR (attributes['http.method'] = ? AND mapContains(attributes, 'http.method') = ?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, countDistinct(multiIf(notEmpty(service), service, NULL)) AS __result_0 FROM event.log WHERE (service = ? OR (attributes['http.method'] = ? AND mapContains(attributes, 'http.method') = ?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "GET", true, "1705226400000000000", uint64(1705224600), "1705485600000000000", uint64(1705485600), testTenant, 10, "redis-manual", "GET", true, "1705226400000000000", uint64(1705224600), "1705485600000000000", uint64(1705485600), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -144,8 +143,8 @@ func TestStatementBuilderTimeSeries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY `service.name` desc LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name` ORDER BY `service.name` desc, ts desc",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1705397400), uint64(1705485600), "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600), 10, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600)},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? GROUP BY `service.name` ORDER BY `service.name` desc LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name` ORDER BY `service.name` desc, ts desc",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1705397400), uint64(1705485600), "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600), testTenant, 10, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -177,8 +176,8 @@ func TestStatementBuilderTimeSeries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(mapContains(attributes, 'materialized.key.name') = ?, attributes['materialized.key.name'], NULL)) AS `materialized.key.name`, count() AS __result_0 FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? GROUP BY `materialized.key.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(mapContains(attributes, 'materialized.key.name') = ?, attributes['materialized.key.name'], NULL)) AS `materialized.key.name`, count() AS __result_0 FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND (`materialized.key.name`) GLOBAL IN (SELECT `materialized.key.name` FROM __limit_cte) GROUP BY ts, `materialized.key.name`",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1705397400), uint64(1705485600), true, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600), 10, true, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600)},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(mapContains(attributes, 'materialized.key.name') = ?, attributes['materialized.key.name'], NULL)) AS `materialized.key.name`, count() AS __result_0 FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? GROUP BY `materialized.key.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(mapContains(attributes, 'materialized.key.name') = ?, attributes['materialized.key.name'], NULL)) AS `materialized.key.name`, count() AS __result_0 FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? AND (`materialized.key.name`) GLOBAL IN (SELECT `materialized.key.name` FROM __limit_cte) GROUP BY ts, `materialized.key.name`",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1705397400), uint64(1705485600), true, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600), testTenant, 10, true, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600), testTenant},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -200,14 +199,14 @@ func TestStatementBuilderTimeSeries(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, count() AS __result_0 FROM event.log WHERE ((match(attributes['materialized.key.name'], ?) AND mapContains(attributes, 'materialized.key.name') = ?) OR (attributes['materialized.key.name'] = ? AND mapContains(attributes, 'materialized.key.name') = ?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? GROUP BY ts",
|
||||
Args: []any{"redis.*", true, "memcached", true, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600)},
|
||||
Query: "SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, count() AS __result_0 FROM event.log WHERE ((match(attributes['materialized.key.name'], ?) AND mapContains(attributes, 'materialized.key.name') = ?) OR (attributes['materialized.key.name'] = ? AND mapContains(attributes, 'materialized.key.name') = ?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? GROUP BY ts",
|
||||
Args: []any{"redis.*", true, "memcached", true, "1705399200000000000", uint64(1705397400), "1705485600000000000", uint64(1705485600), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx := tenantCtx()
|
||||
fl := flaggertest.New(t)
|
||||
|
||||
mockMetadataStore := telemetrytypestest.NewMockMetadataStore()
|
||||
@@ -273,8 +272,8 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -301,8 +300,8 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? ORDER BY attributes['materialized.key.name'] AS `materialized.key.name` desc LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? ORDER BY attributes['materialized.key.name'] AS `materialized.key.name` desc LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -329,8 +328,8 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE ((match(attributes['materialized.key.name'], ?) AND mapContains(attributes, 'materialized.key.name') = ?) OR (attributes['materialized.key.name'] = ? AND mapContains(attributes, 'materialized.key.name') = ?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? ORDER BY attributes['materialized.key.name'] AS `materialized.key.name` desc LIMIT ?",
|
||||
Args: []any{"redis.*", true, "memcached", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE ((match(attributes['materialized.key.name'], ?) AND mapContains(attributes, 'materialized.key.name') = ?) OR (attributes['materialized.key.name'] = ? AND mapContains(attributes, 'materialized.key.name') = ?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? ORDER BY attributes['materialized.key.name'] AS `materialized.key.name` desc LIMIT ?",
|
||||
Args: []any{"redis.*", true, "memcached", true, "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -352,7 +351,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx := tenantCtx()
|
||||
fl := flaggertest.New(t)
|
||||
mockMetadataStore := telemetrytypestest.NewMockMetadataStore()
|
||||
fm := NewFieldMapper(fl)
|
||||
@@ -419,8 +418,8 @@ func TestStatementBuilderListQueryResourceTests(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE match(LOWER(body), LOWER(?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"hello", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE match(LOWER(body), LOWER(?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"hello", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -447,8 +446,8 @@ func TestStatementBuilderListQueryResourceTests(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND match(LOWER(body), LOWER(?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? ORDER BY attributes['materialized.key.name'] AS `materialized.key.name` desc LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "hello", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND match(LOWER(body), LOWER(?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? ORDER BY attributes['materialized.key.name'] AS `materialized.key.name` desc LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "hello", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -463,8 +462,8 @@ func TestStatementBuilderListQueryResourceTests(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE (JSON_VALUE(body, '$.\"status\"') = ? AND JSON_EXISTS(body, '$.\"status\"')) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"success", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE (JSON_VALUE(body, '$.\"status\"') = ? AND JSON_EXISTS(body, '$.\"status\"')) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"success", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -479,8 +478,8 @@ func TestStatementBuilderListQueryResourceTests(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE ((JSONExtract(JSON_QUERY(body, '$.\"user_names\"[*]'), 'Array(String)') = ?) AND JSON_EXISTS(body, '$.\"user_names\"[*]')) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"john_doe", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE ((JSONExtract(JSON_QUERY(body, '$.\"user_names\"[*]'), 'Array(String)') = ?) AND JSON_EXISTS(body, '$.\"user_names\"[*]')) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"john_doe", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -495,14 +494,14 @@ func TestStatementBuilderListQueryResourceTests(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE has(JSONExtract(JSON_QUERY(body, '$.\"user_names\"[*]'), 'Array(String)'), ?) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"john_doe", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE has(JSONExtract(JSON_QUERY(body, '$.\"user_names\"[*]'), 'Array(String)'), ?) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"john_doe", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx := tenantCtx()
|
||||
fl := flaggertest.New(t)
|
||||
mockMetadataStore := telemetrytypestest.NewMockMetadataStore()
|
||||
fm := NewFieldMapper(fl)
|
||||
@@ -581,7 +580,7 @@ func TestStatementBuilderTimeSeriesBodyGroupBy(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx := tenantCtx()
|
||||
fl := flaggertest.New(t)
|
||||
mockMetadataStore := telemetrytypestest.NewMockMetadataStore()
|
||||
fm := NewFieldMapper(fl)
|
||||
@@ -644,8 +643,8 @@ func TestStatementBuilderListQueryServiceCollision(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE ((simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (body ILIKE ?) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "%error%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE ((simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?)) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND (body ILIKE ?) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "%error%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
expectWarn: true,
|
||||
@@ -673,15 +672,15 @@ func TestStatementBuilderListQueryServiceCollision(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND body ILIKE ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? ORDER BY attributes['materialized.key.name'] AS `materialized.key.name` desc LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "%error%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.log_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND body ILIKE ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? ORDER BY attributes['materialized.key.name'] AS `materialized.key.name` desc LIMIT ?",
|
||||
Args: []any{"cartservice", "%service.name%", "%service.name\":\"cartservice%", uint64(1747945619), uint64(1747983448), "%error%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
expectWarn: true,
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx := tenantCtx()
|
||||
mockMetadataStore := telemetrytypestest.NewMockMetadataStore()
|
||||
fl := flaggertest.New(t)
|
||||
fm := NewFieldMapper(fl)
|
||||
@@ -968,8 +967,8 @@ func TestStmtBuilderBodyField(t *testing.T) {
|
||||
},
|
||||
enableUseJSONBody: true,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE body_v2.message <> ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE body_v2.message <> ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{bodySearchDefaultWarning},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -984,8 +983,8 @@ func TestStmtBuilderBodyField(t *testing.T) {
|
||||
},
|
||||
enableUseJSONBody: false,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE body <> ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE body <> ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -999,8 +998,8 @@ func TestStmtBuilderBodyField(t *testing.T) {
|
||||
},
|
||||
enableUseJSONBody: true,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE body_v2.message = ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE body_v2.message = ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{bodySearchDefaultWarning},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -1015,8 +1014,8 @@ func TestStmtBuilderBodyField(t *testing.T) {
|
||||
},
|
||||
enableUseJSONBody: false,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE body = ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE body = ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -1030,8 +1029,8 @@ func TestStmtBuilderBodyField(t *testing.T) {
|
||||
},
|
||||
enableUseJSONBody: true,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE body_v2.message ILIKE ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"%error%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE body_v2.message ILIKE ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"%error%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{bodySearchDefaultWarning},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -1046,8 +1045,8 @@ func TestStmtBuilderBodyField(t *testing.T) {
|
||||
},
|
||||
enableUseJSONBody: false,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE body ILIKE ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"%error%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE body ILIKE ? AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"%error%", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -1079,7 +1078,7 @@ func TestStmtBuilderBodyField(t *testing.T) {
|
||||
100000,
|
||||
)
|
||||
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
if c.expectedErr != nil {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), c.expectedErr.Error())
|
||||
@@ -1116,8 +1115,8 @@ func TestStmtBuilderBodyFullTextSearch(t *testing.T) {
|
||||
},
|
||||
enableUseJSONBody: true,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE match(LOWER(body_v2.message), LOWER(?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"error", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE match(LOWER(body_v2.message), LOWER(?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"error", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{querybuilder.BodyFullTextSearchDefaultWarning},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -1132,8 +1131,8 @@ func TestStmtBuilderBodyFullTextSearch(t *testing.T) {
|
||||
},
|
||||
enableUseJSONBody: true,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE match(LOWER(body_v2.message), LOWER(?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"error", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body_v2 as body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE match(LOWER(body_v2.message), LOWER(?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"error", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
Warnings: []string{querybuilder.BodyFullTextSearchDefaultWarning},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -1148,8 +1147,8 @@ func TestStmtBuilderBodyFullTextSearch(t *testing.T) {
|
||||
},
|
||||
enableUseJSONBody: false,
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE match(LOWER(body), LOWER(?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"error", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), 10},
|
||||
Query: "SELECT toUnixTimestamp64Nano(time) AS timestamp, id, trace_id, span_id, toUInt32OrZero(attributes['trace_flags']) AS trace_flags, severity_text, severity_number, attributes['scope.name'] AS scope_name, attributes['scope.version'] AS scope_version, body, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string, CAST(map() AS Map(String, String)) AS scope_string FROM event.log WHERE match(LOWER(body), LOWER(?)) AND time >= ? AND ts_bucket_start >= ? AND time < ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"error", "1747947419000000000", uint64(1747945619), "1747983448000000000", uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -1181,7 +1180,7 @@ func TestStmtBuilderBodyFullTextSearch(t *testing.T) {
|
||||
100000,
|
||||
)
|
||||
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
if c.expectedErr != nil {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), c.expectedErr.Error())
|
||||
@@ -1220,7 +1219,7 @@ func TestSkipResourceFingerprintLogs(t *testing.T) {
|
||||
t.Run("disabled uses the legacy CTE", func(t *testing.T) {
|
||||
sb := newSkipResourceFingerprintLogsBuilder(t, nil, false, threshold)
|
||||
|
||||
stmt, err := sb.Build(context.Background(), startMs, endMs, qbtypes.RequestTypeRaw, query, nil)
|
||||
stmt, err := sb.Build(tenantCtx(), startMs, endMs, qbtypes.RequestTypeRaw, query, nil)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, stmt.Query, "__resource_filter AS (SELECT fingerprint")
|
||||
require.Contains(t, stmt.Query, "resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter)")
|
||||
@@ -1237,7 +1236,7 @@ func TestSkipResourceFingerprintLogs(t *testing.T) {
|
||||
|
||||
sb := newSkipResourceFingerprintLogsBuilder(t, mockStore, true, threshold)
|
||||
|
||||
stmt, err := sb.Build(context.Background(), startMs, endMs, qbtypes.RequestTypeRaw, query, nil)
|
||||
stmt, err := sb.Build(tenantCtx(), startMs, endMs, qbtypes.RequestTypeRaw, query, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Contains(t, stmt.Query, "__resource_filter AS (SELECT fingerprint")
|
||||
@@ -1257,7 +1256,7 @@ func TestSkipResourceFingerprintLogs(t *testing.T) {
|
||||
|
||||
sb := newSkipResourceFingerprintLogsBuilder(t, mockStore, true, threshold)
|
||||
|
||||
stmt, err := sb.Build(context.Background(), startMs, endMs, qbtypes.RequestTypeRaw, query, nil)
|
||||
stmt, err := sb.Build(tenantCtx(), startMs, endMs, qbtypes.RequestTypeRaw, query, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotContains(t, stmt.Query, "__resource_filter AS")
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package telemetrylogs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hanzoai/o11y/pkg/errors"
|
||||
"github.com/hanzoai/o11y/pkg/flagger/flaggertest"
|
||||
"github.com/hanzoai/o11y/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/hanzoai/o11y/pkg/querybuilder"
|
||||
"github.com/hanzoai/o11y/pkg/types/authtypes"
|
||||
qbtypes "github.com/hanzoai/o11y/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/hanzoai/o11y/pkg/types/telemetrytypes"
|
||||
"github.com/hanzoai/o11y/pkg/types/telemetrytypes/telemetrytypestest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testTenant is the org the statement tests read as; every event.log read
|
||||
// they build binds it as `org = ?`.
|
||||
const testTenant = "acme"
|
||||
|
||||
func tenantCtx() context.Context {
|
||||
return authtypes.NewContextWithTenant(context.Background(), testTenant)
|
||||
}
|
||||
|
||||
func newTenantTestBuilder(t *testing.T) qbtypes.StatementBuilder[qbtypes.LogAggregation] {
|
||||
fl := flaggertest.New(t)
|
||||
store := telemetrytypestest.NewMockMetadataStore()
|
||||
store.KeysMap = buildCompleteFieldKeyMap(time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC))
|
||||
fm := NewFieldMapper(fl)
|
||||
cb := NewConditionBuilder(fm, fl)
|
||||
rewriter := querybuilder.NewAggExprRewriter(instrumentationtest.New().ToProviderSettings(), nil, fm, cb, nil, fl)
|
||||
return NewLogQueryStatementBuilder(instrumentationtest.New().ToProviderSettings(), store, fm, cb, rewriter,
|
||||
DefaultFullTextColumn, GetBodyJSONKey, fl, nil, false, 100000)
|
||||
}
|
||||
|
||||
// Every request type reads event.log for exactly one org, and none reads it
|
||||
// for no org.
|
||||
func TestLogReadsAreScopedToTheTenant(t *testing.T) {
|
||||
b := newTenantTestBuilder(t)
|
||||
query := qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
StepInterval: qbtypes.Step{Duration: 30e9},
|
||||
Aggregations: []qbtypes.LogAggregation{{Expression: "count()"}},
|
||||
Limit: 10,
|
||||
}
|
||||
for _, rt := range []qbtypes.RequestType{qbtypes.RequestTypeRaw, qbtypes.RequestTypeTimeSeries, qbtypes.RequestTypeScalar} {
|
||||
t.Run(rt.StringValue(), func(t *testing.T) {
|
||||
stmt, err := b.Build(tenantCtx(), 1747947419000, 1747983448000, rt, query, nil)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, stmt.Query, "org = ?")
|
||||
require.Contains(t, stmt.Args, testTenant)
|
||||
|
||||
_, err = b.Build(context.Background(), 1747947419000, 1747983448000, rt, query, nil)
|
||||
require.Error(t, err)
|
||||
require.True(t, errors.Ast(err, errors.TypeForbidden), "a read with no tenant is refused as forbidden, got %v", err)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -94,7 +94,7 @@ func TestLiveProofTraces(t *testing.T) {
|
||||
Order: []qbtypes.OrderBy{{Key: qbtypes.OrderByKey{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "time"}}, Direction: qbtypes.OrderDirectionDesc}},
|
||||
Limit: 10,
|
||||
}
|
||||
stmt, err := statementBuilder.Build(context.Background(), start, end, qbtypes.RequestTypeRaw, q, nil)
|
||||
stmt, err := statementBuilder.Build(tenantCtx(), start, end, qbtypes.RequestTypeRaw, q, nil)
|
||||
require.NoError(t, err)
|
||||
n := execCount(t, ts, *stmt)
|
||||
t.Logf("span list rows=%d\nSQL: %s\nARGS: %v", n, stmt.Query, stmt.Args)
|
||||
@@ -107,7 +107,7 @@ func TestLiveProofTraces(t *testing.T) {
|
||||
Filter: &qbtypes.Filter{Expression: "kind_string = 'client' AND has_error = true AND duration_nano > 1000"},
|
||||
Limit: 10,
|
||||
}
|
||||
stmt, err := statementBuilder.Build(context.Background(), start, end, qbtypes.RequestTypeRaw, q, nil)
|
||||
stmt, err := statementBuilder.Build(tenantCtx(), start, end, qbtypes.RequestTypeRaw, q, nil)
|
||||
require.NoError(t, err)
|
||||
n := execCount(t, ts, *stmt)
|
||||
t.Logf("intrinsic rows=%d\nSQL: %s\nARGS: %v", n, stmt.Query, stmt.Args)
|
||||
@@ -123,7 +123,7 @@ func TestLiveProofTraces(t *testing.T) {
|
||||
GroupBy: []qbtypes.GroupByKey{{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "service.name"}}},
|
||||
Limit: 10,
|
||||
}
|
||||
stmt, err := statementBuilder.Build(context.Background(), start, end, qbtypes.RequestTypeTimeSeries, q, nil)
|
||||
stmt, err := statementBuilder.Build(tenantCtx(), start, end, qbtypes.RequestTypeTimeSeries, q, nil)
|
||||
require.NoError(t, err)
|
||||
n := execCount(t, ts, *stmt)
|
||||
t.Logf("timeseries rows=%d\nSQL: %s\nARGS: %v", n, stmt.Query, stmt.Args)
|
||||
@@ -136,7 +136,7 @@ func TestLiveProofTraces(t *testing.T) {
|
||||
Filter: &qbtypes.Filter{Expression: "service.name = 'o11y-writeside-proof'"},
|
||||
Limit: 10,
|
||||
}
|
||||
stmt, err := statementBuilder.Build(context.Background(), start, end, qbtypes.RequestTypeTrace, q, nil)
|
||||
stmt, err := statementBuilder.Build(tenantCtx(), start, end, qbtypes.RequestTypeTrace, q, nil)
|
||||
require.NoError(t, err)
|
||||
n := execCount(t, ts, *stmt)
|
||||
t.Logf("trace query rows=%d\nSQL: %s\nARGS: %v", n, stmt.Query, stmt.Args)
|
||||
@@ -149,7 +149,7 @@ func TestLiveProofTraces(t *testing.T) {
|
||||
Filter: &qbtypes.Filter{Expression: "isroot = true"},
|
||||
Limit: 5,
|
||||
}
|
||||
stmt, err := statementBuilder.Build(context.Background(), start, end, qbtypes.RequestTypeRaw, q, nil)
|
||||
stmt, err := statementBuilder.Build(tenantCtx(), start, end, qbtypes.RequestTypeRaw, q, nil)
|
||||
require.NoError(t, err)
|
||||
n := execCount(t, ts, *stmt)
|
||||
t.Logf("isroot rows=%d\nSQL: %s\nARGS: %v", n, stmt.Query, stmt.Args)
|
||||
@@ -162,7 +162,7 @@ func TestLiveProofTraces(t *testing.T) {
|
||||
Filter: &qbtypes.Filter{Expression: "isentrypoint = true"},
|
||||
Limit: 5,
|
||||
}
|
||||
stmt, err := statementBuilder.Build(context.Background(), start, end, qbtypes.RequestTypeRaw, q, nil)
|
||||
stmt, err := statementBuilder.Build(tenantCtx(), start, end, qbtypes.RequestTypeRaw, q, nil)
|
||||
require.NoError(t, err)
|
||||
n := execCount(t, ts, *stmt)
|
||||
t.Logf("isentrypoint rows=%d\nSQL: %s\nARGS: %v", n, stmt.Query, stmt.Args)
|
||||
|
||||
@@ -412,6 +412,11 @@ func (b *traceQueryStatementBuilder) buildTraceQuery(
|
||||
innerSB.L("time", fmt.Sprintf("%d", end)),
|
||||
innerSB.GE("ts_bucket_start", startBucket),
|
||||
innerSB.LE("ts_bucket_start", endBucket))
|
||||
innerTenant, err := querybuilder.TenantCondition(ctx, innerSB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
innerSB.Where(innerTenant)
|
||||
|
||||
// order by duration and limit 1 per trace (duration_nano is the SELECT alias)
|
||||
innerSB.OrderBy("duration_nano DESC")
|
||||
@@ -784,6 +789,12 @@ func (b *traceQueryStatementBuilder) addFilterCondition(
|
||||
|
||||
sb.Where(sb.GE("time", fmt.Sprintf("%d", start)), sb.L("time", fmt.Sprintf("%d", end)), sb.GE("ts_bucket_start", startBucket), sb.LE("ts_bucket_start", endBucket))
|
||||
|
||||
tenant, err := querybuilder.TenantCondition(ctx, sb)
|
||||
if err != nil {
|
||||
return preparedWhereClause, err
|
||||
}
|
||||
sb.Where(tenant)
|
||||
|
||||
return preparedWhereClause, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package telemetrytraces
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -67,8 +66,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -96,8 +95,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE (service = ? OR (attributes['http.request.method'] = ? AND mapContains(attributes, 'http.request.method') = ?)) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE (service = ? OR (attributes['http.request.method'] = ? AND mapContains(attributes, 'http.request.method') = ?)) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "GET", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "redis-manual", "GET", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE (service = ? OR (attributes['http.request.method'] = ? AND mapContains(attributes, 'http.request.method') = ?)) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE (service = ? OR (attributes['http.request.method'] = ? AND mapContains(attributes, 'http.request.method') = ?)) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "GET", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10, "redis-manual", "GET", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -125,8 +124,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE ((match(attributes['materialized.key.name'], ?) AND mapContains(attributes, 'materialized.key.name') = ?) OR service = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE ((match(attributes['materialized.key.name'], ?) AND mapContains(attributes, 'materialized.key.name') = ?) OR service = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", true, "redis-manual", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "redis-manual", true, "redis-manual", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE ((match(attributes['materialized.key.name'], ?) AND mapContains(attributes, 'materialized.key.name') = ?) OR service = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE ((match(attributes['materialized.key.name'], ?) AND mapContains(attributes, 'materialized.key.name') = ?) OR service = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", true, "redis-manual", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10, "redis-manual", true, "redis-manual", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -156,8 +155,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(attributes['http.route'] <> ?, attributes['http.route'], NULL)) AS `httpRoute`, count() AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `httpRoute` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(attributes['http.route'] <> ?, attributes['http.route'], NULL)) AS `httpRoute`, count() AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`httpRoute`) GLOBAL IN (SELECT `httpRoute` FROM __limit_cte) GROUP BY ts, `httpRoute`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(attributes['http.route'] <> ?, attributes['http.route'], NULL)) AS `httpRoute`, count() AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? GROUP BY `httpRoute` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(attributes['http.route'] <> ?, attributes['http.route'], NULL)) AS `httpRoute`, count() AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND (`httpRoute`) GLOBAL IN (SELECT `httpRoute` FROM __limit_cte) GROUP BY ts, `httpRoute`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10, "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -194,8 +193,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(attributes['http.route'] <> ?, attributes['http.route'], NULL)) AS `httpRoute`, toString(multiIf(if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) <> ?, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']), NULL)) AS `httpMethod`, count() AS __result_0 FROM event.span WHERE (service = ? AND if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) <> ? AND kind = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `httpRoute`, `httpMethod` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(attributes['http.route'] <> ?, attributes['http.route'], NULL)) AS `httpRoute`, toString(multiIf(if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) <> ?, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']), NULL)) AS `httpMethod`, count() AS __result_0 FROM event.span WHERE (service = ? AND if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) <> ? AND kind = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`httpRoute`, `httpMethod`) GLOBAL IN (SELECT `httpRoute`, `httpMethod` FROM __limit_cte) GROUP BY ts, `httpRoute`, `httpMethod`",
|
||||
Args: []any{"", "", "redis-manual", "", "Server", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", "", "redis-manual", "", "Server", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(attributes['http.route'] <> ?, attributes['http.route'], NULL)) AS `httpRoute`, toString(multiIf(if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) <> ?, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']), NULL)) AS `httpMethod`, count() AS __result_0 FROM event.span WHERE (service = ? AND if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) <> ? AND kind = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? GROUP BY `httpRoute`, `httpMethod` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(attributes['http.route'] <> ?, attributes['http.route'], NULL)) AS `httpRoute`, toString(multiIf(if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) <> ?, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']), NULL)) AS `httpMethod`, count() AS __result_0 FROM event.span WHERE (service = ? AND if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) <> ? AND kind = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND (`httpRoute`, `httpMethod`) GLOBAL IN (SELECT `httpRoute`, `httpMethod` FROM __limit_cte) GROUP BY ts, `httpRoute`, `httpMethod`",
|
||||
Args: []any{"", "", "redis-manual", "", "Server", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10, "", "", "redis-manual", "", "Server", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -235,8 +234,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, sum(multiIf(mapContains(attributes, 'metric.max_count') = ?, toFloat64(toFloat64OrNull(attributes['metric.max_count'])), NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 desc LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, sum(multiIf(mapContains(attributes, 'metric.max_count') = ?, toFloat64(toFloat64OrNull(attributes['metric.max_count'])), NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name` ORDER BY ts desc",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, sum(multiIf(mapContains(attributes, 'metric.max_count') = ?, toFloat64(toFloat64OrNull(attributes['metric.max_count'])), NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? GROUP BY `service.name` ORDER BY __result_0 desc LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, sum(multiIf(mapContains(attributes, 'metric.max_count') = ?, toFloat64(toFloat64OrNull(attributes['metric.max_count'])), NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name` ORDER BY ts desc",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -264,8 +263,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, sum(multiIf(mapContains(attributes, 'cart.items_count') = ?, toFloat64(toFloat64OrNull(attributes['cart.items_count'])), NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, sum(multiIf(mapContains(attributes, 'cart.items_count') = ?, toFloat64(toFloat64OrNull(attributes['cart.items_count'])), NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, sum(multiIf(mapContains(attributes, 'cart.items_count') = ?, toFloat64(toFloat64OrNull(attributes['cart.items_count'])), NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, sum(multiIf(mapContains(attributes, 'cart.items_count') = ?, toFloat64(toFloat64OrNull(attributes['cart.items_count'])), NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -303,8 +302,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, sum(multiIf(mapContains(attributes, 'cart.items_count') = ?, toFloat64(toFloat64OrNull(attributes['cart.items_count'])), NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY `service.name` desc LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, sum(multiIf(mapContains(attributes, 'cart.items_count') = ?, toFloat64(toFloat64OrNull(attributes['cart.items_count'])), NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name` ORDER BY `service.name` desc, ts desc",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, sum(multiIf(mapContains(attributes, 'cart.items_count') = ?, toFloat64(toFloat64OrNull(attributes['cart.items_count'])), NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? GROUP BY `service.name` ORDER BY `service.name` desc LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, sum(multiIf(mapContains(attributes, 'cart.items_count') = ?, toFloat64(toFloat64OrNull(attributes['cart.items_count'])), NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name` ORDER BY `service.name` desc, ts desc",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10, true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -334,8 +333,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) <> ?, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']), NULL)) AS `responseStatusCode`, count() AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `responseStatusCode` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) <> ?, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']), NULL)) AS `responseStatusCode`, count() AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`responseStatusCode`) GLOBAL IN (SELECT `responseStatusCode` FROM __limit_cte) GROUP BY ts, `responseStatusCode`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) <> ?, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']), NULL)) AS `responseStatusCode`, count() AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? GROUP BY `responseStatusCode` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) <> ?, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']), NULL)) AS `responseStatusCode`, count() AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND (`responseStatusCode`) GLOBAL IN (SELECT `responseStatusCode` FROM __limit_cte) GROUP BY ts, `responseStatusCode`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10, "", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -365,8 +364,8 @@ func TestStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) <> ?, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']), NULL)) AS `responseStatusCode`, quantile(0.90)(multiIf(duration <> ?, duration, NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `responseStatusCode` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) <> ?, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']), NULL)) AS `responseStatusCode`, quantile(0.90)(multiIf(duration <> ?, duration, NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`responseStatusCode`) GLOBAL IN (SELECT `responseStatusCode` FROM __limit_cte) GROUP BY ts, `responseStatusCode`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "", 0, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "", 0, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __limit_cte AS (SELECT toString(multiIf(if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) <> ?, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']), NULL)) AS `responseStatusCode`, quantile(0.90)(multiIf(duration <> ?, duration, NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? GROUP BY `responseStatusCode` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) <> ?, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']), NULL)) AS `responseStatusCode`, quantile(0.90)(multiIf(duration <> ?, duration, NULL)) AS __result_0 FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND (`responseStatusCode`) GLOBAL IN (SELECT `responseStatusCode` FROM __limit_cte) GROUP BY ts, `responseStatusCode`",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "", 0, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10, "", 0, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -400,7 +399,7 @@ func TestStatementBuilder(t *testing.T) {
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, c.requestType, c.query, vars)
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, c.requestType, c.query, vars)
|
||||
|
||||
if c.expectedErr != nil {
|
||||
require.Error(t, err)
|
||||
@@ -462,8 +461,8 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, name AS `name`, service AS `service.name`, duration AS `duration_nano`, toFloat64OrNull(attributes['cart.items_count']) AS `cart.items_count` FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, name AS `name`, service AS `service.name`, duration AS `duration_nano`, toFloat64OrNull(attributes['cart.items_count']) AS `cart.items_count` FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -491,8 +490,8 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, attributes['trace_state'] AS `trace_state`, parent AS `parent_span_id`, toUInt32OrZero(attributes['flags']) AS `flags`, name AS `name`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, duration AS `duration_nano`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY attributes['user.id'] AS `user.id` desc LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, attributes['trace_state'] AS `trace_state`, parent AS `parent_span_id`, toUInt32OrZero(attributes['flags']) AS `flags`, name AS `name`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, duration AS `duration_nano`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? ORDER BY attributes['user.id'] AS `user.id` desc LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -535,8 +534,8 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, name AS `name`, service AS `serviceName`, duration AS `durationNano`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `httpMethod`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `responseStatusCode` FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, name AS `name`, service AS `serviceName`, duration AS `durationNano`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `httpMethod`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `responseStatusCode` FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -579,8 +578,8 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, name AS `name`, service AS `serviceName`, duration AS `durationNano`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `httpMethod`, multiIf(toString(attributes['mixed.materialization.key']) != '', toString(attributes['mixed.materialization.key']), toString(attributes['mixed.materialization.key']) != '', toString(attributes['mixed.materialization.key']), NULL) AS `mixed.materialization.key` FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, name AS `name`, service AS `serviceName`, duration AS `durationNano`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `httpMethod`, multiIf(toString(attributes['mixed.materialization.key']) != '', toString(attributes['mixed.materialization.key']), toString(attributes['mixed.materialization.key']) != '', toString(attributes['mixed.materialization.key']), NULL) AS `mixed.materialization.key` FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -624,8 +623,8 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, name AS `name`, service AS `serviceName`, duration AS `durationNano`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `httpMethod`, attributes['mixed.materialization.key'] AS `mixed.materialization.key` FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, name AS `name`, service AS `serviceName`, duration AS `durationNano`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `httpMethod`, attributes['mixed.materialization.key'] AS `mixed.materialization.key` FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -692,7 +691,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
|
||||
if c.expectedErr != nil {
|
||||
require.Error(t, err)
|
||||
@@ -746,8 +745,8 @@ func TestStatementBuilderListQueryWithCorruptData(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, attributes['trace_state'] AS `trace_state`, parent AS `parent_span_id`, toUInt32OrZero(attributes['flags']) AS `flags`, name AS `name`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, duration AS `duration_nano`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, attributes['trace_state'] AS `trace_state`, parent AS `parent_span_id`, toUInt32OrZero(attributes['flags']) AS `flags`, name AS `name`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, duration AS `duration_nano`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? LIMIT ?",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -788,8 +787,8 @@ func TestStatementBuilderListQueryWithCorruptData(t *testing.T) {
|
||||
}},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, attributes['trace_state'] AS `trace_state`, parent AS `parent_span_id`, toUInt32OrZero(attributes['flags']) AS `flags`, name AS `name`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, duration AS `duration_nano`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY time AS `timestamp` asc LIMIT ?",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "SELECT time AS `timestamp`, trace_id AS `trace_id`, span_id AS `span_id`, attributes['trace_state'] AS `trace_state`, parent AS `parent_span_id`, toUInt32OrZero(attributes['flags']) AS `flags`, name AS `name`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, duration AS `duration_nano`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? ORDER BY time AS `timestamp` asc LIMIT ?",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -819,7 +818,7 @@ func TestStatementBuilderListQueryWithCorruptData(t *testing.T) {
|
||||
100000,
|
||||
)
|
||||
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
|
||||
if c.expectedErr != nil {
|
||||
require.Error(t, err)
|
||||
@@ -848,8 +847,8 @@ func TestStatementBuilderGroupByResourceEvolution(t *testing.T) {
|
||||
startMs: 1747947419000, // 2025-05-22 21:56:59 UTC, ~3m before release
|
||||
endMs: 1747983448000, // 2025-05-23 07:57:28 UTC, ~10h after release
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -857,8 +856,8 @@ func TestStatementBuilderGroupByResourceEvolution(t *testing.T) {
|
||||
startMs: 1747960000000, // 2025-05-23 00:26:40 UTC, ~2.5h after release
|
||||
endMs: 1747983448000, // 2025-05-23 07:57:28 UTC
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"1747960000000000000", "1747983448000000000", uint64(1747958200), uint64(1747983448), 10, "1747960000000000000", "1747983448000000000", uint64(1747958200), uint64(1747983448)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"1747960000000000000", "1747983448000000000", uint64(1747958200), uint64(1747983448), testTenant, 10, "1747960000000000000", "1747983448000000000", uint64(1747958200), uint64(1747983448), testTenant},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -866,8 +865,8 @@ func TestStatementBuilderGroupByResourceEvolution(t *testing.T) {
|
||||
startMs: 1747900000000, // 2025-05-22 08:26:40 UTC, ~13.5h before release
|
||||
endMs: 1747947000000, // 2025-05-22 21:50:00 UTC, ~10m before release
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"1747900000000000000", "1747947000000000000", uint64(1747898200), uint64(1747947000), 10, "1747900000000000000", "1747947000000000000", uint64(1747898200), uint64(1747947000)},
|
||||
Query: "WITH __limit_cte AS (SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? GROUP BY `service.name` ORDER BY __result_0 DESC LIMIT ?) SELECT toStartOfInterval(time, INTERVAL 30 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND (`service.name`) GLOBAL IN (SELECT `service.name` FROM __limit_cte) GROUP BY ts, `service.name`",
|
||||
Args: []any{"1747900000000000000", "1747947000000000000", uint64(1747898200), uint64(1747947000), testTenant, 10, "1747900000000000000", "1747947000000000000", uint64(1747898200), uint64(1747947000), testTenant},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -910,7 +909,7 @@ func TestStatementBuilderGroupByResourceEvolution(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
q, err := statementBuilder.Build(context.Background(), c.startMs, c.endMs, qbtypes.RequestTypeTimeSeries, query, nil)
|
||||
q, err := statementBuilder.Build(tenantCtx(), c.startMs, c.endMs, qbtypes.RequestTypeTimeSeries, query, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c.expected.Query, q.Query)
|
||||
require.Equal(t, c.expected.Args, q.Args)
|
||||
@@ -938,8 +937,8 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __toe AS (SELECT trace_id FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND trace_id GLOBAL IN __toe AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __resource_filter AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), __toe AS (SELECT trace_id FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND trace_id GLOBAL IN __toe AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -954,8 +953,8 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM event.span WHERE (attributes['materialized.key.name'] = ? AND mapContains(attributes, 'materialized.key.name') = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND trace_id GLOBAL IN __toe AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"redis-manual", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM event.span WHERE (attributes['materialized.key.name'] = ? AND mapContains(attributes, 'materialized.key.name') = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND trace_id GLOBAL IN __toe AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"redis-manual", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -970,8 +969,8 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM event.span WHERE ((match(attributes['materialized.key.name'], ?) AND mapContains(attributes, 'materialized.key.name') = ?) OR service = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND trace_id GLOBAL IN __toe AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"redis-manual", true, "redis-manual", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM event.span WHERE ((match(attributes['materialized.key.name'], ?) AND mapContains(attributes, 'materialized.key.name') = ?) OR service = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND trace_id GLOBAL IN __toe AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"redis-manual", true, "redis-manual", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -984,8 +983,8 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -1000,8 +999,8 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM event.span WHERE ((name, service) GLOBAL IN (SELECT DISTINCT name, serviceName from event.operation WHERE time >= toDateTime(1747947419))) AND parent != '' AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND trace_id GLOBAL IN __toe AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM event.span WHERE ((name, service) GLOBAL IN (SELECT DISTINCT name, serviceName from event.operation WHERE time >= toDateTime(1747947419))) AND parent != '' AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND trace_id GLOBAL IN __toe AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -1016,8 +1015,8 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM event.span WHERE (((name, service) GLOBAL IN (SELECT DISTINCT name, serviceName from event.operation WHERE time >= toDateTime(1747947419))) AND parent != '' OR (attributes['materialized.key.name'] = ? AND mapContains(attributes, 'materialized.key.name') = ?)) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND trace_id GLOBAL IN __toe AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"redis-manual", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM event.span WHERE (((name, service) GLOBAL IN (SELECT DISTINCT name, serviceName from event.operation WHERE time >= toDateTime(1747947419))) AND parent != '' OR (attributes['materialized.key.name'] = ? AND mapContains(attributes, 'materialized.key.name') = ?)) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND trace_id GLOBAL IN __toe AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"redis-manual", true, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -1032,8 +1031,8 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
Limit: 10,
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM event.span WHERE (toFloat64(toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0))) = ? OR kind = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND trace_id GLOBAL IN __toe AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{float64(2), "Server", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH __toe AS (SELECT trace_id FROM event.span WHERE (toFloat64(toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0))) = ? OR kind = ?) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __toe_duration_sorted AS (SELECT trace_id, duration AS duration_nano, service as `service.name`, name FROM event.span WHERE parent = '' AND trace_id GLOBAL IN __toe AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? ORDER BY duration_nano DESC LIMIT 1 BY trace_id) SELECT __toe_duration_sorted.`service.name` AS `service.name`, __toe_duration_sorted.name AS `name`, count() AS span_count, __toe_duration_sorted.duration_nano AS `duration_nano`, __toe_duration_sorted.trace_id AS `trace_id` FROM __toe INNER JOIN __toe_duration_sorted ON __toe.trace_id = __toe_duration_sorted.trace_id GROUP BY trace_id, duration_nano, name, `service.name` ORDER BY duration_nano DESC LIMIT 1 BY trace_id LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{float64(2), "Server", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -1061,7 +1060,7 @@ func TestStatementBuilderTraceQuery(t *testing.T) {
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
||||
q, err := statementBuilder.Build(context.Background(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
q, err := statementBuilder.Build(tenantCtx(), 1747947419000, 1747983448000, c.requestType, c.query, nil)
|
||||
|
||||
if c.expectedErr != nil {
|
||||
require.Error(t, err)
|
||||
@@ -1617,7 +1616,7 @@ func TestSkipResourceFingerprint(t *testing.T) {
|
||||
t.Run("disabled uses the legacy CTE", func(t *testing.T) {
|
||||
sb := newSkipResourceFingerprintBuilder(t, nil, false, threshold)
|
||||
|
||||
stmt, err := sb.Build(context.Background(), startMs, endMs, qbtypes.RequestTypeRaw, query, nil)
|
||||
stmt, err := sb.Build(tenantCtx(), startMs, endMs, qbtypes.RequestTypeRaw, query, nil)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, stmt.Query, "__resource_filter AS (SELECT fingerprint")
|
||||
require.Contains(t, stmt.Query, "resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter)")
|
||||
@@ -1636,7 +1635,7 @@ func TestSkipResourceFingerprint(t *testing.T) {
|
||||
|
||||
sb := newSkipResourceFingerprintBuilder(t, mockStore, true, threshold)
|
||||
|
||||
stmt, err := sb.Build(context.Background(), startMs, endMs, qbtypes.RequestTypeRaw, query, nil)
|
||||
stmt, err := sb.Build(tenantCtx(), startMs, endMs, qbtypes.RequestTypeRaw, query, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Contains(t, stmt.Query, "__resource_filter AS (SELECT fingerprint")
|
||||
@@ -1656,7 +1655,7 @@ func TestSkipResourceFingerprint(t *testing.T) {
|
||||
|
||||
sb := newSkipResourceFingerprintBuilder(t, mockStore, true, threshold)
|
||||
|
||||
stmt, err := sb.Build(context.Background(), startMs, endMs, qbtypes.RequestTypeRaw, query, nil)
|
||||
stmt, err := sb.Build(tenantCtx(), startMs, endMs, qbtypes.RequestTypeRaw, query, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotContains(t, stmt.Query, "__resource_filter AS")
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package telemetrytraces
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hanzoai/o11y/pkg/errors"
|
||||
"github.com/hanzoai/o11y/pkg/flagger/flaggertest"
|
||||
"github.com/hanzoai/o11y/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/hanzoai/o11y/pkg/querybuilder"
|
||||
"github.com/hanzoai/o11y/pkg/types/authtypes"
|
||||
qbtypes "github.com/hanzoai/o11y/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/hanzoai/o11y/pkg/types/telemetrytypes"
|
||||
"github.com/hanzoai/o11y/pkg/types/telemetrytypes/telemetrytypestest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testTenant is the org the statement tests read as; every event.span read
|
||||
// they build binds it as `org = ?`.
|
||||
const testTenant = "acme"
|
||||
|
||||
func tenantCtx() context.Context {
|
||||
return authtypes.NewContextWithTenant(context.Background(), testTenant)
|
||||
}
|
||||
|
||||
// Every request type reads event.span for exactly one org, and none reads it
|
||||
// for no org. The trace request type reads spans twice (matching traces and
|
||||
// their root spans), and both reads are scoped.
|
||||
func TestSpanReadsAreScopedToTheTenant(t *testing.T) {
|
||||
fl := flaggertest.New(t)
|
||||
store := telemetrytypestest.NewMockMetadataStore()
|
||||
store.KeysMap = buildCompleteFieldKeyMap(time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC))
|
||||
fm := NewFieldMapper()
|
||||
cb := NewConditionBuilder(fm)
|
||||
rewriter := querybuilder.NewAggExprRewriter(instrumentationtest.New().ToProviderSettings(), nil, fm, cb, nil, fl)
|
||||
b := NewTraceQueryStatementBuilder(instrumentationtest.New().ToProviderSettings(), store, fm, cb, rewriter, nil, fl, false, 100000)
|
||||
|
||||
query := qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
||||
Signal: telemetrytypes.SignalTraces,
|
||||
StepInterval: qbtypes.Step{Duration: 30e9},
|
||||
Aggregations: []qbtypes.TraceAggregation{{Expression: "count()"}},
|
||||
Limit: 10,
|
||||
}
|
||||
cases := map[qbtypes.RequestType]int{
|
||||
qbtypes.RequestTypeRaw: 1,
|
||||
qbtypes.RequestTypeTimeSeries: 1,
|
||||
qbtypes.RequestTypeScalar: 1,
|
||||
qbtypes.RequestTypeTrace: 2,
|
||||
}
|
||||
for rt, reads := range cases {
|
||||
t.Run(rt.StringValue(), func(t *testing.T) {
|
||||
stmt, err := b.Build(tenantCtx(), 1747947419000, 1747983448000, rt, query, nil)
|
||||
require.NoError(t, err)
|
||||
require.GreaterOrEqual(t, countOf(stmt.Args, testTenant), reads)
|
||||
|
||||
_, err = b.Build(context.Background(), 1747947419000, 1747983448000, rt, query, nil)
|
||||
require.Error(t, err)
|
||||
require.True(t, errors.Ast(err, errors.TypeForbidden), "a read with no tenant is refused as forbidden, got %v", err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func countOf(args []any, v any) int {
|
||||
n := 0
|
||||
for _, a := range args {
|
||||
if a == v {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
@@ -63,7 +63,9 @@ func (b *traceOperatorCTEBuilder) collectQueries() error {
|
||||
|
||||
func (b *traceOperatorCTEBuilder) build(ctx context.Context, requestType qbtypes.RequestType) (*qbtypes.Statement, error) {
|
||||
|
||||
b.buildAllSpansCTE(ctx)
|
||||
if err := b.buildAllSpansCTE(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rootCTEName, err := b.buildExpressionCTEs(ctx, b.operator.ParsedExpression)
|
||||
if err != nil {
|
||||
@@ -131,7 +133,7 @@ func (b *traceOperatorCTEBuilder) build(ctx context.Context, requestType qbtypes
|
||||
}
|
||||
|
||||
// Will be used in Indirect descendant Query, will not be used in any other query.
|
||||
func (b *traceOperatorCTEBuilder) buildAllSpansCTE(ctx context.Context) {
|
||||
func (b *traceOperatorCTEBuilder) buildAllSpansCTE(ctx context.Context) error {
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("*")
|
||||
sb.SelectMore("service AS `service.name`")
|
||||
@@ -139,15 +141,21 @@ func (b *traceOperatorCTEBuilder) buildAllSpansCTE(ctx context.Context) {
|
||||
sb.From(fmt.Sprintf("%s.%s", DBName, SpanTableName))
|
||||
startBucket := b.start/querybuilder.NsToSeconds - querybuilder.BucketAdjustment
|
||||
endBucket := b.end / querybuilder.NsToSeconds
|
||||
tenant, err := querybuilder.TenantCondition(ctx, sb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb.Where(
|
||||
sb.GE("time", fmt.Sprintf("%d", b.start)),
|
||||
sb.L("time", fmt.Sprintf("%d", b.end)),
|
||||
sb.GE("ts_bucket_start", startBucket),
|
||||
sb.LE("ts_bucket_start", endBucket),
|
||||
tenant,
|
||||
)
|
||||
sql, args := sb.BuildWithFlavor(datastoresql.Flavor)
|
||||
b.stmtBuilder.logger.DebugContext(ctx, "Built all_spans CTE")
|
||||
b.addCTE("all_spans", sql, args, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *traceOperatorCTEBuilder) buildTimeConstantsCTE() string {
|
||||
@@ -250,11 +258,16 @@ func (b *traceOperatorCTEBuilder) buildQueryCTE(ctx context.Context, queryName s
|
||||
}
|
||||
startBucket := b.start/querybuilder.NsToSeconds - querybuilder.BucketAdjustment
|
||||
endBucket := b.end / querybuilder.NsToSeconds
|
||||
tenant, err := querybuilder.TenantCondition(ctx, sb)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sb.Where(
|
||||
sb.GE("time", fmt.Sprintf("%d", b.start)),
|
||||
sb.L("time", fmt.Sprintf("%d", b.end)),
|
||||
sb.GE("ts_bucket_start", startBucket),
|
||||
sb.LE("ts_bucket_start", endBucket),
|
||||
tenant,
|
||||
)
|
||||
|
||||
if query.Filter != nil && query.Filter.Expression != "" {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package telemetrytraces
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -86,8 +85,8 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, service AS `service.name` FROM A_DIR_DESC_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, service AS `service.name` FROM A_DIR_DESC_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -123,8 +122,8 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM A_INDIR_DESC_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "gateway", "%service.name%", "%service.name\":\"gateway%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 5},
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM A_INDIR_DESC_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "gateway", "%service.name%", "%service.name\":\"gateway%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 5},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -160,8 +159,8 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_AND_B AS (SELECT l.* FROM A AS l INNER JOIN B AS r ON l.trace_id = r.trace_id) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM A_AND_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 15},
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), A_AND_B AS (SELECT l.* FROM A AS l INNER JOIN B AS r ON l.trace_id = r.trace_id) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM A_AND_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 15},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -197,8 +196,8 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_OR_B AS (SELECT * FROM A UNION DISTINCT SELECT * FROM B) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM A_OR_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 20},
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), A_OR_B AS (SELECT * FROM A UNION DISTINCT SELECT * FROM B) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM A_OR_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 20},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -234,8 +233,8 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_not_B AS (SELECT l.* FROM A AS l WHERE l.trace_id GLOBAL NOT IN (SELECT DISTINCT trace_id FROM B)) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM A_not_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), A_not_B AS (SELECT l.* FROM A AS l WHERE l.trace_id GLOBAL NOT IN (SELECT DISTINCT trace_id FROM B)) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM A_not_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -283,8 +282,8 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent) SELECT toStartOfInterval(time, INTERVAL 60 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM A_DIR_DESC_B GROUP BY ts, `service.name` ORDER BY ts desc SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448)},
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent) SELECT toStartOfInterval(time, INTERVAL 60 SECOND) AS ts, toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, count() AS __result_0 FROM A_DIR_DESC_B GROUP BY ts, `service.name` ORDER BY ts desc SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -342,8 +341,8 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), B AS (SELECT * FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND toFloat64(if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code'])) < ?), A_AND_B AS (SELECT l.* FROM A AS l INNER JOIN B AS r ON l.trace_id = r.trace_id) SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, avg(multiIf(duration <> ?, duration, NULL)) AS __result_0 FROM A_AND_B GROUP BY `service.name` ORDER BY __result_0 desc SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), float64(400), 0},
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), B AS (SELECT * FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ? AND toFloat64(if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code'])) < ?), A_AND_B AS (SELECT l.* FROM A AS l INNER JOIN B AS r ON l.trace_id = r.trace_id) SELECT toString(multiIf(notEmpty(service), service, NULL)) AS `service.name`, avg(multiIf(duration <> ?, duration, NULL)) AS __result_0 FROM A_AND_B GROUP BY `service.name` ORDER BY __result_0 desc SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, float64(400), 0},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -399,8 +398,8 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent), __resource_filter_C AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), C AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_C) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_D AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), D AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_D) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), C_DIR_DESC_D AS (SELECT p.* FROM C AS p INNER JOIN D AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent), A_DIR_DESC_B_AND_C_DIR_DESC_D AS (SELECT l.* FROM A_DIR_DESC_B AS l INNER JOIN C_DIR_DESC_D AS r ON l.trace_id = r.trace_id) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM A_DIR_DESC_B_AND_C_DIR_DESC_D ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "auth", "%service.name%", "%service.name\":\"auth%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 5},
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent), __resource_filter_C AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), C AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_C) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_D AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), D AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_D) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), C_DIR_DESC_D AS (SELECT p.* FROM C AS p INNER JOIN D AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent), A_DIR_DESC_B_AND_C_DIR_DESC_D AS (SELECT l.* FROM A_DIR_DESC_B AS l INNER JOIN C_DIR_DESC_D AS r ON l.trace_id = r.trace_id) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM A_DIR_DESC_B_AND_C_DIR_DESC_D ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "auth", "%service.name%", "%service.name\":\"auth%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 5},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -433,8 +432,8 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id), __return_from_B AS (SELECT * FROM B WHERE trace_id IN (SELECT DISTINCT trace_id FROM A_INDIR_DESC_B)) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM __return_from_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "gateway", "%service.name%", "%service.name\":\"gateway%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id), __return_from_B AS (SELECT * FROM B WHERE trace_id IN (SELECT DISTINCT trace_id FROM A_INDIR_DESC_B)) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM __return_from_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "gateway", "%service.name%", "%service.name\":\"gateway%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -475,8 +474,8 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: qbtypes.Statement{
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id), __resource_filter_C AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), C AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_C) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_INDIR_DESC_B_AND_C AS (SELECT l.* FROM A_INDIR_DESC_B AS l INNER JOIN C AS r ON l.trace_id = r.trace_id), __return_from_C AS (SELECT * FROM C WHERE trace_id IN (SELECT DISTINCT trace_id FROM A_INDIR_DESC_B_AND_C)) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM __return_from_C ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "gateway", "%service.name%", "%service.name\":\"gateway%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "auth", "%service.name%", "%service.name\":\"auth%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
|
||||
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, service AS `service.name` FROM event.span WHERE time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_A AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), __resource_filter_B AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id), __resource_filter_C AS (SELECT fingerprint FROM event.span_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), C AS (SELECT * FROM event.span WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_C) AND time >= ? AND time < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND org = ?), A_INDIR_DESC_B_AND_C AS (SELECT l.* FROM A_INDIR_DESC_B AS l INNER JOIN C AS r ON l.trace_id = r.trace_id), __return_from_C AS (SELECT * FROM C WHERE trace_id IN (SELECT DISTINCT trace_id FROM A_INDIR_DESC_B_AND_C)) SELECT time AS `timestamp`, trace_id, span_id, name, duration AS `duration_nano`, parent AS `parent_span_id`, attributes['trace_state'] AS `trace_state`, toUInt32OrZero(attributes['flags']) AS `flags`, toInt8(multiIf(kind = 'internal', 1, kind = 'server', 2, kind = 'client', 3, kind = 'producer', 4, kind = 'consumer', 5, 0)) AS `kind`, kind AS `kind_string`, toInt16(multiIf(status = 'error', 2, status = 'ok', 1, 0)) AS `status_code`, attributes['status.message'] AS `status_message`, status AS `status_code_string`, JSONExtractArrayRaw(attributes['span.events']) AS `events`, attributes['links'] AS `links`, if(attributes['http.response.status_code'] != '', attributes['http.response.status_code'], attributes['http.status_code']) AS `response_status_code`, url AS `external_http_url`, url AS `http_url`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `external_http_method`, if(attributes['http.request.method'] != '', attributes['http.request.method'], attributes['http.method']) AS `http_method`, host AS `http_host`, attributes['db.name'] AS `db_name`, attributes['db.operation'] AS `db_operation`, toBool(status = 'error') AS `has_error`, attributes['is_remote'] AS `is_remote`, attributes AS attributes_string, CAST(map() AS Map(String, Float64)) AS attributes_number, CAST(map() AS Map(String, Bool)) AS attributes_bool, map('service.name', toString(service), 'host', toString(host)) AS resources_string FROM __return_from_C ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
|
||||
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "gateway", "%service.name%", "%service.name\":\"gateway%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, "auth", "%service.name%", "%service.name\":\"auth%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), testTenant, 10},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
@@ -491,7 +490,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
q, err := statementBuilder.Build(
|
||||
context.Background(),
|
||||
tenantCtx(),
|
||||
1747947419000,
|
||||
1747983448000,
|
||||
c.requestType,
|
||||
@@ -581,7 +580,7 @@ func TestTraceOperatorStatementBuilderErrors(t *testing.T) {
|
||||
err := c.operator.ParseExpression()
|
||||
if err == nil { // Only proceed if parsing succeeded
|
||||
_, err = statementBuilder.Build(
|
||||
context.Background(),
|
||||
tenantCtx(),
|
||||
1747947419000,
|
||||
1747983448000,
|
||||
qbtypes.RequestTypeRaw,
|
||||
@@ -656,7 +655,7 @@ func TestTraceOperatorStatementBuilderAdjustsKeys(t *testing.T) {
|
||||
}
|
||||
|
||||
q, err := statementBuilder.Build(
|
||||
context.Background(),
|
||||
tenantCtx(),
|
||||
1747947419000,
|
||||
1747983448000,
|
||||
c.requestType,
|
||||
@@ -710,7 +709,7 @@ func TestTraceOperatorStatementBuilderDeduplicatesKeys(t *testing.T) {
|
||||
require.NoError(t, operator.ParseExpression())
|
||||
|
||||
q, err := statementBuilder.Build(
|
||||
context.Background(),
|
||||
tenantCtx(),
|
||||
1747947419000,
|
||||
1747983448000,
|
||||
qbtypes.RequestTypeScalar,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package telemetrytraces
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -110,7 +109,7 @@ func TestTraceTimeRangeOptimization(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx := tenantCtx()
|
||||
|
||||
stmt, err := statementBuilder.Build(
|
||||
ctx,
|
||||
|
||||
@@ -32,6 +32,9 @@ type Identity struct {
|
||||
OrgID valuer.UUID `json:"orgId"`
|
||||
IdenNProvider IdentNProvider `json:"identNProvider"`
|
||||
Email valuer.Email `json:"email"`
|
||||
// Tenant is the org slug the identity's warehouse reads are scoped to. Only
|
||||
// the Hanzo IAM provider sets it, from the validated X-Org-Id.
|
||||
Tenant string `json:"tenant,omitempty"`
|
||||
}
|
||||
|
||||
func NewIdentity(userID valuer.UUID, serviceAccountID valuer.UUID, principal Principal, orgID valuer.UUID, email valuer.Email, identNProvider IdentNProvider) *Identity {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package authtypes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/hanzoai/o11y/pkg/errors"
|
||||
)
|
||||
|
||||
// The tenant is the org slug every warehouse row carries in its `org` column
|
||||
// (event.log, event.span). A read of those tables binds `org = ?` to it, so a
|
||||
// caller only ever reads the rows of the org it was admitted as.
|
||||
//
|
||||
// For a request it is the validated X-Org-Id the Hanzo IAM provider resolved
|
||||
// (Identity.Tenant); for a scheduled rule it is the name of the rule's own org.
|
||||
type tenantKey struct{}
|
||||
|
||||
// NewContextWithTenant attaches the tenant a warehouse read is scoped to.
|
||||
func NewContextWithTenant(ctx context.Context, tenant string) context.Context {
|
||||
return context.WithValue(ctx, tenantKey{}, strings.TrimSpace(tenant))
|
||||
}
|
||||
|
||||
// TenantFromContext returns the tenant, or a forbidden error when none was
|
||||
// attached: a warehouse read without one would read every org's rows.
|
||||
func TenantFromContext(ctx context.Context) (string, error) {
|
||||
tenant, _ := ctx.Value(tenantKey{}).(string)
|
||||
if tenant == "" {
|
||||
return "", errors.NewForbiddenf(errors.CodeForbidden, "no tenant: a telemetry read must be scoped to one org")
|
||||
}
|
||||
return tenant, nil
|
||||
}
|
||||
Reference in New Issue
Block a user