@mastra/duckdb 1.12.1 → 1.13.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +6 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/{observability-DlmDRPW3.cjs → observability-BCEHa8GU.cjs} +296 -42
- package/dist/observability-BCEHa8GU.cjs.map +1 -0
- package/dist/{observability-BmLO_YTO.js → observability-TpgAucZM.js} +296 -42
- package/dist/observability-TpgAucZM.js.map +1 -0
- package/dist/storage/domains/observability/index.d.ts +3 -2
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/trace-aggregate.d.ts +17 -0
- package/dist/storage/domains/observability/trace-aggregate.d.ts.map +1 -0
- package/dist/storage/domains/observability/trace-query.d.ts +18 -0
- package/dist/storage/domains/observability/trace-query.d.ts.map +1 -1
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/observability-BmLO_YTO.js.map +0 -1
- package/dist/observability-DlmDRPW3.cjs.map +0 -1
|
@@ -2703,6 +2703,15 @@ const TRACE_STATUS_SQL = `CASE WHEN r.error IS NOT NULL THEN 'error' ELSE 'succe
|
|
|
2703
2703
|
function durationMsSql(startedAt, endedAt) {
|
|
2704
2704
|
return `date_diff('millisecond', ${startedAt}, ${endedAt})`;
|
|
2705
2705
|
}
|
|
2706
|
+
/**
|
|
2707
|
+
* Top-level string metadata value of the trace root (alias `r`). The SQL binds the JSON path
|
|
2708
|
+
* twice; use `traceMetadataPathValues` for the matching parameters.
|
|
2709
|
+
*/
|
|
2710
|
+
const TRACE_METADATA_VALUE_SQL = `NULLIF(trim(CASE WHEN json_type(r.metadata, ?) = 'VARCHAR' THEN json_extract_string(r.metadata, ?) END), '')`;
|
|
2711
|
+
function traceMetadataPathValues(key) {
|
|
2712
|
+
const path = `$.${JSON.stringify(key)}`;
|
|
2713
|
+
return [path, path];
|
|
2714
|
+
}
|
|
2706
2715
|
const TRACE_FIELDS = {
|
|
2707
2716
|
traceId: {
|
|
2708
2717
|
sql: "r.traceId",
|
|
@@ -2969,13 +2978,11 @@ function compileScalarPredicate(predicate, registry, allowMetadata = false) {
|
|
|
2969
2978
|
let fieldValues = [];
|
|
2970
2979
|
if (isMetadataField(predicate.field)) {
|
|
2971
2980
|
if (!allowMetadata) throw new Error(`Unsupported trusted trace-query field: ${predicate.field}`);
|
|
2972
|
-
const key = predicate.field.slice(9);
|
|
2973
|
-
const path = `$.${JSON.stringify(key)}`;
|
|
2974
2981
|
field = {
|
|
2975
|
-
sql:
|
|
2982
|
+
sql: TRACE_METADATA_VALUE_SQL,
|
|
2976
2983
|
parameterType: "scalar"
|
|
2977
2984
|
};
|
|
2978
|
-
fieldValues =
|
|
2985
|
+
fieldValues = traceMetadataPathValues(predicate.field.slice(9));
|
|
2979
2986
|
} else field = fieldDefinition(registry, predicate.field);
|
|
2980
2987
|
if (predicate.type === "presence") return {
|
|
2981
2988
|
sql: `${field.sql} IS ${predicate.operator === "exists" ? "NOT " : ""}NULL`,
|
|
@@ -3152,15 +3159,66 @@ function collectThreadRelatedCollections(predicate, collections) {
|
|
|
3152
3159
|
else collectThreadRelatedCollections(predicate.arg, collections);
|
|
3153
3160
|
return collections;
|
|
3154
3161
|
}
|
|
3155
|
-
|
|
3162
|
+
const ROOT_PUSHDOWN_FIELDS = /* @__PURE__ */ new Set([
|
|
3163
|
+
"traceId",
|
|
3164
|
+
"threadId",
|
|
3165
|
+
"resourceId",
|
|
3166
|
+
"runId",
|
|
3167
|
+
"sessionId",
|
|
3168
|
+
"userId",
|
|
3169
|
+
"organizationId",
|
|
3170
|
+
"endedAt",
|
|
3171
|
+
"entityName",
|
|
3172
|
+
"entityType",
|
|
3173
|
+
"environment"
|
|
3174
|
+
]);
|
|
3175
|
+
function isRootPushdownPredicate(predicate) {
|
|
3176
|
+
if (predicate.type === "relation") return false;
|
|
3177
|
+
if (predicate.type === "boolean") return predicate.args.every(isRootPushdownPredicate);
|
|
3178
|
+
if (predicate.type === "not") return isRootPushdownPredicate(predicate.arg);
|
|
3179
|
+
return ROOT_PUSHDOWN_FIELDS.has(predicate.field);
|
|
3180
|
+
}
|
|
3181
|
+
/** Top-level AND conjuncts of `where` that only read root-row columns. */
|
|
3182
|
+
function rootPushdownPredicates(predicate) {
|
|
3183
|
+
if (!predicate) return [];
|
|
3184
|
+
return (predicate.type === "boolean" && predicate.operator === "and" ? predicate.args : [predicate]).filter(isRootPushdownPredicate);
|
|
3185
|
+
}
|
|
3186
|
+
/**
|
|
3187
|
+
* Builds the selection CTEs. `root_trace_ids` narrows the root scan to traces that could have a
|
|
3188
|
+
* qualifying current root before the window functions run: some root event carries the current
|
|
3189
|
+
* root's own conditions (ended, tenant, pushed `where` conjuncts) and some root event has a
|
|
3190
|
+
* timestamp in the window (the current root's `startedAt` is a root event's timestamp in its
|
|
3191
|
+
* trace). This only removes traces that cannot qualify; `root_events` / `current_roots` still see
|
|
3192
|
+
* every root event of the remaining traces, and the full `where` is applied in `candidates`.
|
|
3193
|
+
*/
|
|
3194
|
+
function compileDuckDBTraceScope(relatedCollections, scope, timeRange, rootPredicates = []) {
|
|
3156
3195
|
const tenantConditions = (alias) => scope ? [`${alias}.organizationId = ?`, ...scope.resourceId === void 0 ? [] : [`${alias}.resourceId = ?`]] : [];
|
|
3157
3196
|
const tenantValues = scope ? scope.resourceId === void 0 ? [scope.organizationId] : [scope.organizationId, scope.resourceId] : [];
|
|
3158
3197
|
const tenantWhere = (alias) => {
|
|
3159
3198
|
const conditions = tenantConditions(alias);
|
|
3160
3199
|
return conditions.length ? `\n WHERE ${conditions.join(" AND ")}` : "";
|
|
3161
3200
|
};
|
|
3162
|
-
const
|
|
3201
|
+
const rowConditions = ["r.endedAt IS NOT NULL", ...tenantConditions("r")];
|
|
3202
|
+
const values = [
|
|
3203
|
+
timeRange.from,
|
|
3204
|
+
timeRange.to,
|
|
3205
|
+
...tenantValues
|
|
3206
|
+
];
|
|
3207
|
+
for (const predicate of rootPredicates) {
|
|
3208
|
+
const compiled = compilePredicate(predicate);
|
|
3209
|
+
rowConditions.push(`(${compiled.sql})`);
|
|
3210
|
+
values.push(...compiled.values);
|
|
3211
|
+
}
|
|
3212
|
+
values.push(timeRange.from, timeRange.to, ...tenantValues);
|
|
3163
3213
|
const ctes = [
|
|
3214
|
+
`root_trace_ids AS (
|
|
3215
|
+
SELECT r.traceId
|
|
3216
|
+
FROM span_events r
|
|
3217
|
+
WHERE r.parentSpanId IS NULL
|
|
3218
|
+
GROUP BY r.traceId
|
|
3219
|
+
HAVING bool_or(r.timestamp >= CAST(? AS TIMESTAMP) AND r.timestamp < CAST(? AS TIMESTAMP))
|
|
3220
|
+
AND bool_or(${rowConditions.join("\n AND ")})
|
|
3221
|
+
)`,
|
|
3164
3222
|
`root_events AS (
|
|
3165
3223
|
SELECT
|
|
3166
3224
|
*,
|
|
@@ -3170,6 +3228,7 @@ function compileDuckDBTraceScope(relatedCollections, scope) {
|
|
|
3170
3228
|
END AS startedAt
|
|
3171
3229
|
FROM span_events
|
|
3172
3230
|
WHERE parentSpanId IS NULL
|
|
3231
|
+
AND traceId IN (SELECT traceId FROM root_trace_ids)
|
|
3173
3232
|
)`,
|
|
3174
3233
|
`current_roots AS (
|
|
3175
3234
|
SELECT * EXCLUDE (rootRank)
|
|
@@ -3257,28 +3316,30 @@ function compileDuckDBTraceScope(relatedCollections, scope) {
|
|
|
3257
3316
|
values
|
|
3258
3317
|
};
|
|
3259
3318
|
}
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
const conditions = [
|
|
3268
|
-
`r.endedAt IS NOT NULL`,
|
|
3269
|
-
`r.startedAt >= CAST(? AS TIMESTAMP)`,
|
|
3270
|
-
`r.startedAt < CAST(? AS TIMESTAMP)`
|
|
3271
|
-
];
|
|
3319
|
+
/**
|
|
3320
|
+
* Builds the trace selection stages shared by trace queries and trace aggregates: root scope,
|
|
3321
|
+
* related-collection scopes, and a `candidates` CTE projecting `columns` from matching roots.
|
|
3322
|
+
*/
|
|
3323
|
+
function compileDuckDBTraceCandidates(plan, columns) {
|
|
3324
|
+
const { ctes, values } = compileDuckDBTraceScope(collectRelatedCollections(plan.where), plan.scope, plan.timeRange, rootPushdownPredicates(plan.where));
|
|
3325
|
+
let predicateSql = "TRUE";
|
|
3272
3326
|
if (plan.where) {
|
|
3273
3327
|
const predicate = compilePredicate(plan.where);
|
|
3274
|
-
|
|
3328
|
+
predicateSql = `(${predicate.sql})`;
|
|
3275
3329
|
values.push(...predicate.values);
|
|
3276
3330
|
}
|
|
3277
3331
|
ctes.push(`candidates AS (
|
|
3278
|
-
SELECT ${
|
|
3332
|
+
SELECT ${columns}
|
|
3279
3333
|
FROM root_scope r
|
|
3280
|
-
WHERE ${
|
|
3334
|
+
WHERE ${predicateSql}
|
|
3281
3335
|
)`);
|
|
3336
|
+
return {
|
|
3337
|
+
ctes,
|
|
3338
|
+
values
|
|
3339
|
+
};
|
|
3340
|
+
}
|
|
3341
|
+
function compileDuckDBTraceQuery(plan) {
|
|
3342
|
+
const { ctes, values } = compileDuckDBTraceCandidates(plan, `${TRACE_SELECT}${plan.paginationMode === "delta" ? ", r.cursorId AS deltaWatermark" : ""}`);
|
|
3282
3343
|
const candidates = `WITH ${ctes.join(",\n ")}`;
|
|
3283
3344
|
if (plan.result === "groups") {
|
|
3284
3345
|
const pageCondition = plan.cursor ? `AND threadId > ?` : "";
|
|
@@ -3301,7 +3362,7 @@ LIMIT ?`,
|
|
|
3301
3362
|
values.push(watermark ?? "0", plan.limit + 1);
|
|
3302
3363
|
return {
|
|
3303
3364
|
sql: `${candidates},
|
|
3304
|
-
delta_head AS (SELECT coalesce(max(cursorId), 0) AS streamHead FROM
|
|
3365
|
+
delta_head AS (SELECT coalesce(max(cursorId), 0) AS streamHead FROM span_events WHERE parentSpanId IS NULL),
|
|
3305
3366
|
delta_rows AS (
|
|
3306
3367
|
SELECT * FROM candidates
|
|
3307
3368
|
WHERE deltaWatermark > CAST(? AS BIGINT) ${watermark === void 0 ? "AND FALSE" : ""}
|
|
@@ -3331,7 +3392,7 @@ ORDER BY delta_rows.deltaWatermark ASC NULLS LAST, delta_rows.traceId ASC`,
|
|
|
3331
3392
|
SELECT COUNT(*) AS total
|
|
3332
3393
|
FROM candidates
|
|
3333
3394
|
)
|
|
3334
|
-
SELECT page_rows.*, page_total.total${deltaPollingFeatureEnabled() ? ", (SELECT coalesce(max(cursorId), 0) FROM
|
|
3395
|
+
SELECT page_rows.*, page_total.total${deltaPollingFeatureEnabled() ? ", (SELECT coalesce(max(cursorId), 0) FROM span_events WHERE parentSpanId IS NULL) AS streamHead" : ""}
|
|
3335
3396
|
FROM page_total
|
|
3336
3397
|
LEFT JOIN page_rows ON TRUE
|
|
3337
3398
|
ORDER BY page_rows.__row_position ASC NULLS LAST`,
|
|
@@ -3357,12 +3418,7 @@ LIMIT ?`,
|
|
|
3357
3418
|
function compileDuckDBThreadQuery(plan) {
|
|
3358
3419
|
const relatedCollections = collectRelatedCollections(plan.traces.where);
|
|
3359
3420
|
collectThreadRelatedCollections(plan.where, relatedCollections);
|
|
3360
|
-
const { ctes, values
|
|
3361
|
-
const values = [
|
|
3362
|
-
plan.traces.timeRange.from,
|
|
3363
|
-
plan.traces.timeRange.to,
|
|
3364
|
-
...scopeValues
|
|
3365
|
-
];
|
|
3421
|
+
const { ctes, values } = compileDuckDBTraceScope(relatedCollections, plan.scope, plan.traces.timeRange);
|
|
3366
3422
|
let eligibilitySql = "TRUE";
|
|
3367
3423
|
if (plan.traces.where) {
|
|
3368
3424
|
const eligibility = compilePredicate(plan.traces.where);
|
|
@@ -3420,12 +3476,7 @@ function discoveryCollections(scope) {
|
|
|
3420
3476
|
return scope === "trace" ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([scope]);
|
|
3421
3477
|
}
|
|
3422
3478
|
function compileDuckDBTraceQueryObservedFields(plan) {
|
|
3423
|
-
const { ctes, values
|
|
3424
|
-
const values = [
|
|
3425
|
-
plan.timeRange.from,
|
|
3426
|
-
plan.timeRange.to,
|
|
3427
|
-
...scopeValues
|
|
3428
|
-
];
|
|
3479
|
+
const { ctes, values } = compileDuckDBTraceScope(/* @__PURE__ */ new Set(), plan.scope, plan.timeRange);
|
|
3429
3480
|
if (plan.search) values.push(plan.search);
|
|
3430
3481
|
values.push(plan.limit + 1);
|
|
3431
3482
|
const search = plan.search ? `AND strpos(lower('metadata.' || entry.key), lower(?)) > 0` : "";
|
|
@@ -3447,12 +3498,7 @@ LIMIT ?`,
|
|
|
3447
3498
|
};
|
|
3448
3499
|
}
|
|
3449
3500
|
function compileDuckDBTraceQueryValues(plan) {
|
|
3450
|
-
const { ctes, values
|
|
3451
|
-
const values = [
|
|
3452
|
-
plan.timeRange.from,
|
|
3453
|
-
plan.timeRange.to,
|
|
3454
|
-
...scopeValues
|
|
3455
|
-
];
|
|
3501
|
+
const { ctes, values } = compileDuckDBTraceScope(discoveryCollections(plan.predicateScope), plan.scope, plan.timeRange);
|
|
3456
3502
|
let fieldSql;
|
|
3457
3503
|
let source = discoverySource(plan.predicateScope);
|
|
3458
3504
|
if (plan.predicateScope === "trace" && plan.path === "tags") {
|
|
@@ -3621,6 +3667,209 @@ async function queryThreads(db, plan) {
|
|
|
3621
3667
|
});
|
|
3622
3668
|
}
|
|
3623
3669
|
//#endregion
|
|
3670
|
+
//#region src/storage/domains/observability/trace-aggregate.ts
|
|
3671
|
+
/** Trace-root column expressions (alias `r`) for each groupable dimension; same values `where` sees. */
|
|
3672
|
+
const DIMENSION_SQL = {
|
|
3673
|
+
entityType: "r.entityType",
|
|
3674
|
+
entityName: "r.entityName",
|
|
3675
|
+
environment: "r.environment",
|
|
3676
|
+
status: TRACE_STATUS_SQL,
|
|
3677
|
+
serviceName: "r.serviceName",
|
|
3678
|
+
executionSource: "r.executionSource",
|
|
3679
|
+
threadId: "r.threadId",
|
|
3680
|
+
resourceId: "r.resourceId",
|
|
3681
|
+
userId: "r.userId",
|
|
3682
|
+
sessionId: "r.sessionId",
|
|
3683
|
+
organizationId: "r.organizationId",
|
|
3684
|
+
experimentId: "r.experimentId"
|
|
3685
|
+
};
|
|
3686
|
+
const PERCENTILES = {
|
|
3687
|
+
"duration.p50": .5,
|
|
3688
|
+
"duration.p90": .9,
|
|
3689
|
+
"duration.p95": .95,
|
|
3690
|
+
"duration.p99": .99
|
|
3691
|
+
};
|
|
3692
|
+
const COMPARISON_SQL = {
|
|
3693
|
+
eq: "=",
|
|
3694
|
+
ne: "<>",
|
|
3695
|
+
lt: "<",
|
|
3696
|
+
lte: "<=",
|
|
3697
|
+
gt: ">",
|
|
3698
|
+
gte: ">="
|
|
3699
|
+
};
|
|
3700
|
+
/**
|
|
3701
|
+
* Compiles a trusted aggregate plan. The plan's caps are enforced by the planner and trusted here.
|
|
3702
|
+
*
|
|
3703
|
+
* Every candidate root is first reduced to a `facts` row (dimension values, duration, error flag,
|
|
3704
|
+
* countDistinct inputs, bucket) so grouping, measures, and joins only reference plain columns.
|
|
3705
|
+
* Without `interval`, groups are filtered, ordered, and limited in one grouped select. With
|
|
3706
|
+
* `interval`, a `ranked` CTE ranks groups on whole-window measures, then only the top `limit`
|
|
3707
|
+
* groups are expanded into per-bucket rows.
|
|
3708
|
+
*
|
|
3709
|
+
* Parameters are positional, so `values` is appended in the order placeholders appear in the text.
|
|
3710
|
+
*/
|
|
3711
|
+
function compileDuckDBTraceAggregate(plan) {
|
|
3712
|
+
const { ctes, values } = compileDuckDBTraceCandidates(plan, "r.*");
|
|
3713
|
+
const dimensionSql = (field) => {
|
|
3714
|
+
if (field.startsWith("metadata.")) return {
|
|
3715
|
+
sql: TRACE_METADATA_VALUE_SQL,
|
|
3716
|
+
values: traceMetadataPathValues(field.slice(9))
|
|
3717
|
+
};
|
|
3718
|
+
const sql = Object.hasOwn(DIMENSION_SQL, field) ? DIMENSION_SQL[field] : void 0;
|
|
3719
|
+
if (sql === void 0) throw new Error(`Unsupported trusted trace-aggregate field: ${field}`);
|
|
3720
|
+
return {
|
|
3721
|
+
sql,
|
|
3722
|
+
values: []
|
|
3723
|
+
};
|
|
3724
|
+
};
|
|
3725
|
+
const distinctSql = (field) => field === "traceId" ? {
|
|
3726
|
+
sql: "r.traceId",
|
|
3727
|
+
values: []
|
|
3728
|
+
} : dimensionSql(field);
|
|
3729
|
+
const dimensionColumns = plan.dimensions.map((_, index) => `d${index}`);
|
|
3730
|
+
const distinctColumns = /* @__PURE__ */ new Map();
|
|
3731
|
+
const factColumns = [];
|
|
3732
|
+
plan.dimensions.forEach((dimension, index) => {
|
|
3733
|
+
const compiled = dimensionSql(dimension);
|
|
3734
|
+
factColumns.push(`CAST(${compiled.sql} AS VARCHAR) AS d${index}`);
|
|
3735
|
+
values.push(...compiled.values);
|
|
3736
|
+
});
|
|
3737
|
+
for (const measure of plan.measures) {
|
|
3738
|
+
if (measure.type !== "countDistinct" || distinctColumns.has(measure.field)) continue;
|
|
3739
|
+
const column = `cd${distinctColumns.size}`;
|
|
3740
|
+
distinctColumns.set(measure.field, column);
|
|
3741
|
+
const compiled = distinctSql(measure.field);
|
|
3742
|
+
factColumns.push(`CAST(${compiled.sql} AS VARCHAR) AS ${column}`);
|
|
3743
|
+
values.push(...compiled.values);
|
|
3744
|
+
}
|
|
3745
|
+
factColumns.push(`CAST(${durationMsSql("r.startedAt", "r.endedAt")} AS DOUBLE) AS durationMs`, `r.error IS NOT NULL AS isError`);
|
|
3746
|
+
if (plan.interval !== void 0) {
|
|
3747
|
+
const intervalMs = coreStorage.TRACE_AGGREGATE_INTERVAL_MS[plan.interval];
|
|
3748
|
+
if (intervalMs === void 0) throw new Error(`Unsupported trusted trace-aggregate interval: ${plan.interval}`);
|
|
3749
|
+
factColumns.push(`epoch_ms((epoch_ms(r.startedAt) // ${intervalMs}) * ${intervalMs}) AS bucket`);
|
|
3750
|
+
}
|
|
3751
|
+
ctes.push(`facts AS (
|
|
3752
|
+
SELECT ${factColumns.join(",\n ")}
|
|
3753
|
+
FROM candidates r
|
|
3754
|
+
)`);
|
|
3755
|
+
const measureSql = (name, alias = "") => {
|
|
3756
|
+
const column = (name) => `${alias}${name}`;
|
|
3757
|
+
if (name === "count") return "CAST(count(*) AS DOUBLE)";
|
|
3758
|
+
if (name === "errorCount") return `CAST(count_if(${column("isError")}) AS DOUBLE)`;
|
|
3759
|
+
if (name === "errorRate") return `CAST(count_if(${column("isError")}) AS DOUBLE) / count(*)`;
|
|
3760
|
+
if (name === "duration.avg") return `avg(${column("durationMs")})`;
|
|
3761
|
+
if (name === "duration.min") return `min(${column("durationMs")})`;
|
|
3762
|
+
if (name === "duration.max") return `max(${column("durationMs")})`;
|
|
3763
|
+
const percentile = PERCENTILES[name];
|
|
3764
|
+
if (percentile !== void 0) return `quantile_cont(${column("durationMs")}, ${percentile})`;
|
|
3765
|
+
const measure = plan.measures.find((candidate) => candidate.type === "countDistinct" && candidate.name === name);
|
|
3766
|
+
const distinctColumn = measure && distinctColumns.get(measure.field);
|
|
3767
|
+
if (distinctColumn === void 0) throw new Error(`Unsupported trusted trace-aggregate measure: ${name}`);
|
|
3768
|
+
return `CAST(count(DISTINCT ${alias}${distinctColumn}) AS DOUBLE)`;
|
|
3769
|
+
};
|
|
3770
|
+
const havingValues = [];
|
|
3771
|
+
const compileHaving = (predicate) => {
|
|
3772
|
+
if (predicate.type === "boolean") return predicate.args.map((arg) => `(${compileHaving(arg)})`).join(predicate.operator === "and" ? " AND " : " OR ");
|
|
3773
|
+
if (predicate.type === "not") return `NOT (${compileHaving(predicate.arg)})`;
|
|
3774
|
+
const measure = measureSql(predicate.measure);
|
|
3775
|
+
if (predicate.type === "membership") {
|
|
3776
|
+
if (predicate.values.length === 0) return predicate.operator === "in" ? "FALSE" : "TRUE";
|
|
3777
|
+
havingValues.push(...predicate.values);
|
|
3778
|
+
const list = predicate.values.map(() => "CAST(? AS DOUBLE)").join(", ");
|
|
3779
|
+
return `${measure} ${predicate.operator === "in" ? "IN" : "NOT IN"} (${list})`;
|
|
3780
|
+
}
|
|
3781
|
+
const operator = COMPARISON_SQL[predicate.operator];
|
|
3782
|
+
if (operator === void 0) throw new Error(`Unsupported trusted trace-aggregate operator: ${predicate.operator}`);
|
|
3783
|
+
havingValues.push(predicate.value);
|
|
3784
|
+
return `${measure} ${operator} CAST(? AS DOUBLE)`;
|
|
3785
|
+
};
|
|
3786
|
+
const havingSql = ["count(*) > 0", ...plan.having ? [`(${compileHaving(plan.having)})`] : []].join(" AND ");
|
|
3787
|
+
const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
|
|
3788
|
+
let orderTarget;
|
|
3789
|
+
if (plan.orderBy.target === "measure") orderTarget = measureSql(plan.orderBy.measure);
|
|
3790
|
+
else {
|
|
3791
|
+
const index = plan.dimensions.indexOf(plan.orderBy.dimension);
|
|
3792
|
+
if (index === -1) throw new Error(`Unsupported trusted trace-aggregate order dimension: ${plan.orderBy.dimension}`);
|
|
3793
|
+
orderTarget = dimensionColumns[index];
|
|
3794
|
+
}
|
|
3795
|
+
const orderSql = [`${orderTarget} ${direction} NULLS LAST`, ...dimensionColumns.map((column) => `${column} ASC NULLS LAST`)].join(", ");
|
|
3796
|
+
const groupSql = dimensionColumns.length > 0 ? `\nGROUP BY ${dimensionColumns.join(", ")}` : "";
|
|
3797
|
+
if (plan.interval === void 0) {
|
|
3798
|
+
const projected = [...dimensionColumns, ...plan.measures.map((measure, index) => `${measureSql(measure.name)} AS m${index}`)];
|
|
3799
|
+
values.push(...havingValues, plan.limit + 1);
|
|
3800
|
+
return {
|
|
3801
|
+
sql: `WITH ${ctes.join(",\n ")}
|
|
3802
|
+
SELECT ${projected.join(", ")}
|
|
3803
|
+
FROM facts${groupSql}
|
|
3804
|
+
HAVING ${havingSql}
|
|
3805
|
+
ORDER BY ${orderSql}
|
|
3806
|
+
LIMIT ?`,
|
|
3807
|
+
values
|
|
3808
|
+
};
|
|
3809
|
+
}
|
|
3810
|
+
ctes.push(`ranked AS (
|
|
3811
|
+
SELECT ${[...dimensionColumns, `row_number() OVER (ORDER BY ${orderSql}) AS rank`].join(", ")}
|
|
3812
|
+
FROM facts${groupSql}
|
|
3813
|
+
HAVING ${havingSql}
|
|
3814
|
+
ORDER BY ${orderSql}
|
|
3815
|
+
LIMIT ?
|
|
3816
|
+
)`);
|
|
3817
|
+
values.push(...havingValues, plan.limit + 1);
|
|
3818
|
+
const joinSql = dimensionColumns.length > 0 ? dimensionColumns.map((column) => `f.${column} IS NOT DISTINCT FROM g.${column}`).join(" AND ") : "TRUE";
|
|
3819
|
+
const projected = [
|
|
3820
|
+
...dimensionColumns.map((column) => `f.${column}`),
|
|
3821
|
+
"f.bucket",
|
|
3822
|
+
...plan.measures.map((measure, index) => `${measureSql(measure.name, "f.")} AS m${index}`),
|
|
3823
|
+
`(SELECT count(*) FROM ranked) > ? AS truncated`
|
|
3824
|
+
];
|
|
3825
|
+
values.push(plan.limit, plan.limit);
|
|
3826
|
+
return {
|
|
3827
|
+
sql: `WITH ${ctes.join(",\n ")}
|
|
3828
|
+
SELECT ${projected.join(", ")}
|
|
3829
|
+
FROM facts f
|
|
3830
|
+
JOIN ranked g ON ${joinSql}
|
|
3831
|
+
WHERE g.rank <= ?
|
|
3832
|
+
GROUP BY ${[
|
|
3833
|
+
"g.rank",
|
|
3834
|
+
...dimensionColumns.map((column) => `f.${column}`),
|
|
3835
|
+
"f.bucket"
|
|
3836
|
+
].join(", ")}
|
|
3837
|
+
ORDER BY g.rank ASC, f.bucket ASC`,
|
|
3838
|
+
values
|
|
3839
|
+
};
|
|
3840
|
+
}
|
|
3841
|
+
async function aggregateTraces(db, plan) {
|
|
3842
|
+
const compiled = compileDuckDBTraceAggregate(plan);
|
|
3843
|
+
let result;
|
|
3844
|
+
try {
|
|
3845
|
+
result = await db.query(compiled.sql, compiled.values);
|
|
3846
|
+
} catch (error) {
|
|
3847
|
+
if (isDuckDBResourceLimit(error)) throw new coreStorage.TraceQueryResourceLimitError();
|
|
3848
|
+
throw error;
|
|
3849
|
+
}
|
|
3850
|
+
let truncated;
|
|
3851
|
+
let resultRows;
|
|
3852
|
+
if (plan.interval === void 0) {
|
|
3853
|
+
truncated = result.length > plan.limit;
|
|
3854
|
+
resultRows = result.slice(0, plan.limit);
|
|
3855
|
+
} else {
|
|
3856
|
+
truncated = result[0]?.truncated === true;
|
|
3857
|
+
resultRows = result;
|
|
3858
|
+
}
|
|
3859
|
+
return {
|
|
3860
|
+
rows: resultRows.map((row) => {
|
|
3861
|
+
const shaped = { measures: Object.fromEntries(plan.measures.map((measure, index) => [measure.name, Number(row[`m${index}`])])) };
|
|
3862
|
+
if (plan.dimensions.length > 0) shaped.dimensions = Object.fromEntries(plan.dimensions.map((dimension, index) => {
|
|
3863
|
+
const value = row[`d${index}`];
|
|
3864
|
+
return [dimension, value == null ? null : String(value)];
|
|
3865
|
+
}));
|
|
3866
|
+
if (plan.interval !== void 0) shaped.bucket = asIsoTimestamp(row.bucket);
|
|
3867
|
+
return shaped;
|
|
3868
|
+
}),
|
|
3869
|
+
truncated
|
|
3870
|
+
};
|
|
3871
|
+
}
|
|
3872
|
+
//#endregion
|
|
3624
3873
|
//#region src/storage/domains/observability/tracing.ts
|
|
3625
3874
|
const COLUMNS_SQL = [
|
|
3626
3875
|
"eventType",
|
|
@@ -4948,6 +5197,7 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends Observ
|
|
|
4948
5197
|
"tag-discovery",
|
|
4949
5198
|
"metric-discovery",
|
|
4950
5199
|
"trace-query",
|
|
5200
|
+
"trace-aggregate",
|
|
4951
5201
|
"trace-query-root-duration",
|
|
4952
5202
|
"trace-query-discovery",
|
|
4953
5203
|
"thread-query",
|
|
@@ -4966,6 +5216,7 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends Observ
|
|
|
4966
5216
|
"metric-discovery",
|
|
4967
5217
|
"delta-polling",
|
|
4968
5218
|
"trace-query",
|
|
5219
|
+
"trace-aggregate",
|
|
4969
5220
|
"trace-query-root-duration",
|
|
4970
5221
|
"trace-query-discovery",
|
|
4971
5222
|
"thread-query",
|
|
@@ -5004,6 +5255,9 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends Observ
|
|
|
5004
5255
|
async queryTraces(plan) {
|
|
5005
5256
|
return queryTraces(this.db, plan);
|
|
5006
5257
|
}
|
|
5258
|
+
async aggregateTraces(plan) {
|
|
5259
|
+
return aggregateTraces(this.db, plan);
|
|
5260
|
+
}
|
|
5007
5261
|
async getTraceQueryObservedFields(plan) {
|
|
5008
5262
|
return getTraceQueryObservedFields(this.db, plan);
|
|
5009
5263
|
}
|
|
@@ -5126,4 +5380,4 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends Observ
|
|
|
5126
5380
|
//#endregion
|
|
5127
5381
|
export { ObservabilityStorageDuckDB };
|
|
5128
5382
|
|
|
5129
|
-
//# sourceMappingURL=observability-
|
|
5383
|
+
//# sourceMappingURL=observability-TpgAucZM.js.map
|