@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
|
@@ -2725,6 +2725,15 @@ const TRACE_STATUS_SQL = `CASE WHEN r.error IS NOT NULL THEN 'error' ELSE 'succe
|
|
|
2725
2725
|
function durationMsSql(startedAt, endedAt) {
|
|
2726
2726
|
return `date_diff('millisecond', ${startedAt}, ${endedAt})`;
|
|
2727
2727
|
}
|
|
2728
|
+
/**
|
|
2729
|
+
* Top-level string metadata value of the trace root (alias `r`). The SQL binds the JSON path
|
|
2730
|
+
* twice; use `traceMetadataPathValues` for the matching parameters.
|
|
2731
|
+
*/
|
|
2732
|
+
const TRACE_METADATA_VALUE_SQL = `NULLIF(trim(CASE WHEN json_type(r.metadata, ?) = 'VARCHAR' THEN json_extract_string(r.metadata, ?) END), '')`;
|
|
2733
|
+
function traceMetadataPathValues(key) {
|
|
2734
|
+
const path = `$.${JSON.stringify(key)}`;
|
|
2735
|
+
return [path, path];
|
|
2736
|
+
}
|
|
2728
2737
|
const TRACE_FIELDS = {
|
|
2729
2738
|
traceId: {
|
|
2730
2739
|
sql: "r.traceId",
|
|
@@ -2991,13 +3000,11 @@ function compileScalarPredicate(predicate, registry, allowMetadata = false) {
|
|
|
2991
3000
|
let fieldValues = [];
|
|
2992
3001
|
if (isMetadataField(predicate.field)) {
|
|
2993
3002
|
if (!allowMetadata) throw new Error(`Unsupported trusted trace-query field: ${predicate.field}`);
|
|
2994
|
-
const key = predicate.field.slice(9);
|
|
2995
|
-
const path = `$.${JSON.stringify(key)}`;
|
|
2996
3003
|
field = {
|
|
2997
|
-
sql:
|
|
3004
|
+
sql: TRACE_METADATA_VALUE_SQL,
|
|
2998
3005
|
parameterType: "scalar"
|
|
2999
3006
|
};
|
|
3000
|
-
fieldValues =
|
|
3007
|
+
fieldValues = traceMetadataPathValues(predicate.field.slice(9));
|
|
3001
3008
|
} else field = fieldDefinition(registry, predicate.field);
|
|
3002
3009
|
if (predicate.type === "presence") return {
|
|
3003
3010
|
sql: `${field.sql} IS ${predicate.operator === "exists" ? "NOT " : ""}NULL`,
|
|
@@ -3174,15 +3181,66 @@ function collectThreadRelatedCollections(predicate, collections) {
|
|
|
3174
3181
|
else collectThreadRelatedCollections(predicate.arg, collections);
|
|
3175
3182
|
return collections;
|
|
3176
3183
|
}
|
|
3177
|
-
|
|
3184
|
+
const ROOT_PUSHDOWN_FIELDS = /* @__PURE__ */ new Set([
|
|
3185
|
+
"traceId",
|
|
3186
|
+
"threadId",
|
|
3187
|
+
"resourceId",
|
|
3188
|
+
"runId",
|
|
3189
|
+
"sessionId",
|
|
3190
|
+
"userId",
|
|
3191
|
+
"organizationId",
|
|
3192
|
+
"endedAt",
|
|
3193
|
+
"entityName",
|
|
3194
|
+
"entityType",
|
|
3195
|
+
"environment"
|
|
3196
|
+
]);
|
|
3197
|
+
function isRootPushdownPredicate(predicate) {
|
|
3198
|
+
if (predicate.type === "relation") return false;
|
|
3199
|
+
if (predicate.type === "boolean") return predicate.args.every(isRootPushdownPredicate);
|
|
3200
|
+
if (predicate.type === "not") return isRootPushdownPredicate(predicate.arg);
|
|
3201
|
+
return ROOT_PUSHDOWN_FIELDS.has(predicate.field);
|
|
3202
|
+
}
|
|
3203
|
+
/** Top-level AND conjuncts of `where` that only read root-row columns. */
|
|
3204
|
+
function rootPushdownPredicates(predicate) {
|
|
3205
|
+
if (!predicate) return [];
|
|
3206
|
+
return (predicate.type === "boolean" && predicate.operator === "and" ? predicate.args : [predicate]).filter(isRootPushdownPredicate);
|
|
3207
|
+
}
|
|
3208
|
+
/**
|
|
3209
|
+
* Builds the selection CTEs. `root_trace_ids` narrows the root scan to traces that could have a
|
|
3210
|
+
* qualifying current root before the window functions run: some root event carries the current
|
|
3211
|
+
* root's own conditions (ended, tenant, pushed `where` conjuncts) and some root event has a
|
|
3212
|
+
* timestamp in the window (the current root's `startedAt` is a root event's timestamp in its
|
|
3213
|
+
* trace). This only removes traces that cannot qualify; `root_events` / `current_roots` still see
|
|
3214
|
+
* every root event of the remaining traces, and the full `where` is applied in `candidates`.
|
|
3215
|
+
*/
|
|
3216
|
+
function compileDuckDBTraceScope(relatedCollections, scope, timeRange, rootPredicates = []) {
|
|
3178
3217
|
const tenantConditions = (alias) => scope ? [`${alias}.organizationId = ?`, ...scope.resourceId === void 0 ? [] : [`${alias}.resourceId = ?`]] : [];
|
|
3179
3218
|
const tenantValues = scope ? scope.resourceId === void 0 ? [scope.organizationId] : [scope.organizationId, scope.resourceId] : [];
|
|
3180
3219
|
const tenantWhere = (alias) => {
|
|
3181
3220
|
const conditions = tenantConditions(alias);
|
|
3182
3221
|
return conditions.length ? `\n WHERE ${conditions.join(" AND ")}` : "";
|
|
3183
3222
|
};
|
|
3184
|
-
const
|
|
3223
|
+
const rowConditions = ["r.endedAt IS NOT NULL", ...tenantConditions("r")];
|
|
3224
|
+
const values = [
|
|
3225
|
+
timeRange.from,
|
|
3226
|
+
timeRange.to,
|
|
3227
|
+
...tenantValues
|
|
3228
|
+
];
|
|
3229
|
+
for (const predicate of rootPredicates) {
|
|
3230
|
+
const compiled = compilePredicate(predicate);
|
|
3231
|
+
rowConditions.push(`(${compiled.sql})`);
|
|
3232
|
+
values.push(...compiled.values);
|
|
3233
|
+
}
|
|
3234
|
+
values.push(timeRange.from, timeRange.to, ...tenantValues);
|
|
3185
3235
|
const ctes = [
|
|
3236
|
+
`root_trace_ids AS (
|
|
3237
|
+
SELECT r.traceId
|
|
3238
|
+
FROM span_events r
|
|
3239
|
+
WHERE r.parentSpanId IS NULL
|
|
3240
|
+
GROUP BY r.traceId
|
|
3241
|
+
HAVING bool_or(r.timestamp >= CAST(? AS TIMESTAMP) AND r.timestamp < CAST(? AS TIMESTAMP))
|
|
3242
|
+
AND bool_or(${rowConditions.join("\n AND ")})
|
|
3243
|
+
)`,
|
|
3186
3244
|
`root_events AS (
|
|
3187
3245
|
SELECT
|
|
3188
3246
|
*,
|
|
@@ -3192,6 +3250,7 @@ function compileDuckDBTraceScope(relatedCollections, scope) {
|
|
|
3192
3250
|
END AS startedAt
|
|
3193
3251
|
FROM span_events
|
|
3194
3252
|
WHERE parentSpanId IS NULL
|
|
3253
|
+
AND traceId IN (SELECT traceId FROM root_trace_ids)
|
|
3195
3254
|
)`,
|
|
3196
3255
|
`current_roots AS (
|
|
3197
3256
|
SELECT * EXCLUDE (rootRank)
|
|
@@ -3279,28 +3338,30 @@ function compileDuckDBTraceScope(relatedCollections, scope) {
|
|
|
3279
3338
|
values
|
|
3280
3339
|
};
|
|
3281
3340
|
}
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
const conditions = [
|
|
3290
|
-
`r.endedAt IS NOT NULL`,
|
|
3291
|
-
`r.startedAt >= CAST(? AS TIMESTAMP)`,
|
|
3292
|
-
`r.startedAt < CAST(? AS TIMESTAMP)`
|
|
3293
|
-
];
|
|
3341
|
+
/**
|
|
3342
|
+
* Builds the trace selection stages shared by trace queries and trace aggregates: root scope,
|
|
3343
|
+
* related-collection scopes, and a `candidates` CTE projecting `columns` from matching roots.
|
|
3344
|
+
*/
|
|
3345
|
+
function compileDuckDBTraceCandidates(plan, columns) {
|
|
3346
|
+
const { ctes, values } = compileDuckDBTraceScope(collectRelatedCollections(plan.where), plan.scope, plan.timeRange, rootPushdownPredicates(plan.where));
|
|
3347
|
+
let predicateSql = "TRUE";
|
|
3294
3348
|
if (plan.where) {
|
|
3295
3349
|
const predicate = compilePredicate(plan.where);
|
|
3296
|
-
|
|
3350
|
+
predicateSql = `(${predicate.sql})`;
|
|
3297
3351
|
values.push(...predicate.values);
|
|
3298
3352
|
}
|
|
3299
3353
|
ctes.push(`candidates AS (
|
|
3300
|
-
SELECT ${
|
|
3354
|
+
SELECT ${columns}
|
|
3301
3355
|
FROM root_scope r
|
|
3302
|
-
WHERE ${
|
|
3356
|
+
WHERE ${predicateSql}
|
|
3303
3357
|
)`);
|
|
3358
|
+
return {
|
|
3359
|
+
ctes,
|
|
3360
|
+
values
|
|
3361
|
+
};
|
|
3362
|
+
}
|
|
3363
|
+
function compileDuckDBTraceQuery(plan) {
|
|
3364
|
+
const { ctes, values } = compileDuckDBTraceCandidates(plan, `${TRACE_SELECT}${plan.paginationMode === "delta" ? ", r.cursorId AS deltaWatermark" : ""}`);
|
|
3304
3365
|
const candidates = `WITH ${ctes.join(",\n ")}`;
|
|
3305
3366
|
if (plan.result === "groups") {
|
|
3306
3367
|
const pageCondition = plan.cursor ? `AND threadId > ?` : "";
|
|
@@ -3323,7 +3384,7 @@ LIMIT ?`,
|
|
|
3323
3384
|
values.push(watermark ?? "0", plan.limit + 1);
|
|
3324
3385
|
return {
|
|
3325
3386
|
sql: `${candidates},
|
|
3326
|
-
delta_head AS (SELECT coalesce(max(cursorId), 0) AS streamHead FROM
|
|
3387
|
+
delta_head AS (SELECT coalesce(max(cursorId), 0) AS streamHead FROM span_events WHERE parentSpanId IS NULL),
|
|
3327
3388
|
delta_rows AS (
|
|
3328
3389
|
SELECT * FROM candidates
|
|
3329
3390
|
WHERE deltaWatermark > CAST(? AS BIGINT) ${watermark === void 0 ? "AND FALSE" : ""}
|
|
@@ -3353,7 +3414,7 @@ ORDER BY delta_rows.deltaWatermark ASC NULLS LAST, delta_rows.traceId ASC`,
|
|
|
3353
3414
|
SELECT COUNT(*) AS total
|
|
3354
3415
|
FROM candidates
|
|
3355
3416
|
)
|
|
3356
|
-
SELECT page_rows.*, page_total.total${deltaPollingFeatureEnabled() ? ", (SELECT coalesce(max(cursorId), 0) FROM
|
|
3417
|
+
SELECT page_rows.*, page_total.total${deltaPollingFeatureEnabled() ? ", (SELECT coalesce(max(cursorId), 0) FROM span_events WHERE parentSpanId IS NULL) AS streamHead" : ""}
|
|
3357
3418
|
FROM page_total
|
|
3358
3419
|
LEFT JOIN page_rows ON TRUE
|
|
3359
3420
|
ORDER BY page_rows.__row_position ASC NULLS LAST`,
|
|
@@ -3379,12 +3440,7 @@ LIMIT ?`,
|
|
|
3379
3440
|
function compileDuckDBThreadQuery(plan) {
|
|
3380
3441
|
const relatedCollections = collectRelatedCollections(plan.traces.where);
|
|
3381
3442
|
collectThreadRelatedCollections(plan.where, relatedCollections);
|
|
3382
|
-
const { ctes, values
|
|
3383
|
-
const values = [
|
|
3384
|
-
plan.traces.timeRange.from,
|
|
3385
|
-
plan.traces.timeRange.to,
|
|
3386
|
-
...scopeValues
|
|
3387
|
-
];
|
|
3443
|
+
const { ctes, values } = compileDuckDBTraceScope(relatedCollections, plan.scope, plan.traces.timeRange);
|
|
3388
3444
|
let eligibilitySql = "TRUE";
|
|
3389
3445
|
if (plan.traces.where) {
|
|
3390
3446
|
const eligibility = compilePredicate(plan.traces.where);
|
|
@@ -3442,12 +3498,7 @@ function discoveryCollections(scope) {
|
|
|
3442
3498
|
return scope === "trace" ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([scope]);
|
|
3443
3499
|
}
|
|
3444
3500
|
function compileDuckDBTraceQueryObservedFields(plan) {
|
|
3445
|
-
const { ctes, values
|
|
3446
|
-
const values = [
|
|
3447
|
-
plan.timeRange.from,
|
|
3448
|
-
plan.timeRange.to,
|
|
3449
|
-
...scopeValues
|
|
3450
|
-
];
|
|
3501
|
+
const { ctes, values } = compileDuckDBTraceScope(/* @__PURE__ */ new Set(), plan.scope, plan.timeRange);
|
|
3451
3502
|
if (plan.search) values.push(plan.search);
|
|
3452
3503
|
values.push(plan.limit + 1);
|
|
3453
3504
|
const search = plan.search ? `AND strpos(lower('metadata.' || entry.key), lower(?)) > 0` : "";
|
|
@@ -3469,12 +3520,7 @@ LIMIT ?`,
|
|
|
3469
3520
|
};
|
|
3470
3521
|
}
|
|
3471
3522
|
function compileDuckDBTraceQueryValues(plan) {
|
|
3472
|
-
const { ctes, values
|
|
3473
|
-
const values = [
|
|
3474
|
-
plan.timeRange.from,
|
|
3475
|
-
plan.timeRange.to,
|
|
3476
|
-
...scopeValues
|
|
3477
|
-
];
|
|
3523
|
+
const { ctes, values } = compileDuckDBTraceScope(discoveryCollections(plan.predicateScope), plan.scope, plan.timeRange);
|
|
3478
3524
|
let fieldSql;
|
|
3479
3525
|
let source = discoverySource(plan.predicateScope);
|
|
3480
3526
|
if (plan.predicateScope === "trace" && plan.path === "tags") {
|
|
@@ -3643,6 +3689,209 @@ async function queryThreads(db, plan) {
|
|
|
3643
3689
|
});
|
|
3644
3690
|
}
|
|
3645
3691
|
//#endregion
|
|
3692
|
+
//#region src/storage/domains/observability/trace-aggregate.ts
|
|
3693
|
+
/** Trace-root column expressions (alias `r`) for each groupable dimension; same values `where` sees. */
|
|
3694
|
+
const DIMENSION_SQL = {
|
|
3695
|
+
entityType: "r.entityType",
|
|
3696
|
+
entityName: "r.entityName",
|
|
3697
|
+
environment: "r.environment",
|
|
3698
|
+
status: TRACE_STATUS_SQL,
|
|
3699
|
+
serviceName: "r.serviceName",
|
|
3700
|
+
executionSource: "r.executionSource",
|
|
3701
|
+
threadId: "r.threadId",
|
|
3702
|
+
resourceId: "r.resourceId",
|
|
3703
|
+
userId: "r.userId",
|
|
3704
|
+
sessionId: "r.sessionId",
|
|
3705
|
+
organizationId: "r.organizationId",
|
|
3706
|
+
experimentId: "r.experimentId"
|
|
3707
|
+
};
|
|
3708
|
+
const PERCENTILES = {
|
|
3709
|
+
"duration.p50": .5,
|
|
3710
|
+
"duration.p90": .9,
|
|
3711
|
+
"duration.p95": .95,
|
|
3712
|
+
"duration.p99": .99
|
|
3713
|
+
};
|
|
3714
|
+
const COMPARISON_SQL = {
|
|
3715
|
+
eq: "=",
|
|
3716
|
+
ne: "<>",
|
|
3717
|
+
lt: "<",
|
|
3718
|
+
lte: "<=",
|
|
3719
|
+
gt: ">",
|
|
3720
|
+
gte: ">="
|
|
3721
|
+
};
|
|
3722
|
+
/**
|
|
3723
|
+
* Compiles a trusted aggregate plan. The plan's caps are enforced by the planner and trusted here.
|
|
3724
|
+
*
|
|
3725
|
+
* Every candidate root is first reduced to a `facts` row (dimension values, duration, error flag,
|
|
3726
|
+
* countDistinct inputs, bucket) so grouping, measures, and joins only reference plain columns.
|
|
3727
|
+
* Without `interval`, groups are filtered, ordered, and limited in one grouped select. With
|
|
3728
|
+
* `interval`, a `ranked` CTE ranks groups on whole-window measures, then only the top `limit`
|
|
3729
|
+
* groups are expanded into per-bucket rows.
|
|
3730
|
+
*
|
|
3731
|
+
* Parameters are positional, so `values` is appended in the order placeholders appear in the text.
|
|
3732
|
+
*/
|
|
3733
|
+
function compileDuckDBTraceAggregate(plan) {
|
|
3734
|
+
const { ctes, values } = compileDuckDBTraceCandidates(plan, "r.*");
|
|
3735
|
+
const dimensionSql = (field) => {
|
|
3736
|
+
if (field.startsWith("metadata.")) return {
|
|
3737
|
+
sql: TRACE_METADATA_VALUE_SQL,
|
|
3738
|
+
values: traceMetadataPathValues(field.slice(9))
|
|
3739
|
+
};
|
|
3740
|
+
const sql = Object.hasOwn(DIMENSION_SQL, field) ? DIMENSION_SQL[field] : void 0;
|
|
3741
|
+
if (sql === void 0) throw new Error(`Unsupported trusted trace-aggregate field: ${field}`);
|
|
3742
|
+
return {
|
|
3743
|
+
sql,
|
|
3744
|
+
values: []
|
|
3745
|
+
};
|
|
3746
|
+
};
|
|
3747
|
+
const distinctSql = (field) => field === "traceId" ? {
|
|
3748
|
+
sql: "r.traceId",
|
|
3749
|
+
values: []
|
|
3750
|
+
} : dimensionSql(field);
|
|
3751
|
+
const dimensionColumns = plan.dimensions.map((_, index) => `d${index}`);
|
|
3752
|
+
const distinctColumns = /* @__PURE__ */ new Map();
|
|
3753
|
+
const factColumns = [];
|
|
3754
|
+
plan.dimensions.forEach((dimension, index) => {
|
|
3755
|
+
const compiled = dimensionSql(dimension);
|
|
3756
|
+
factColumns.push(`CAST(${compiled.sql} AS VARCHAR) AS d${index}`);
|
|
3757
|
+
values.push(...compiled.values);
|
|
3758
|
+
});
|
|
3759
|
+
for (const measure of plan.measures) {
|
|
3760
|
+
if (measure.type !== "countDistinct" || distinctColumns.has(measure.field)) continue;
|
|
3761
|
+
const column = `cd${distinctColumns.size}`;
|
|
3762
|
+
distinctColumns.set(measure.field, column);
|
|
3763
|
+
const compiled = distinctSql(measure.field);
|
|
3764
|
+
factColumns.push(`CAST(${compiled.sql} AS VARCHAR) AS ${column}`);
|
|
3765
|
+
values.push(...compiled.values);
|
|
3766
|
+
}
|
|
3767
|
+
factColumns.push(`CAST(${durationMsSql("r.startedAt", "r.endedAt")} AS DOUBLE) AS durationMs`, `r.error IS NOT NULL AS isError`);
|
|
3768
|
+
if (plan.interval !== void 0) {
|
|
3769
|
+
const intervalMs = _mastra_core_storage.TRACE_AGGREGATE_INTERVAL_MS[plan.interval];
|
|
3770
|
+
if (intervalMs === void 0) throw new Error(`Unsupported trusted trace-aggregate interval: ${plan.interval}`);
|
|
3771
|
+
factColumns.push(`epoch_ms((epoch_ms(r.startedAt) // ${intervalMs}) * ${intervalMs}) AS bucket`);
|
|
3772
|
+
}
|
|
3773
|
+
ctes.push(`facts AS (
|
|
3774
|
+
SELECT ${factColumns.join(",\n ")}
|
|
3775
|
+
FROM candidates r
|
|
3776
|
+
)`);
|
|
3777
|
+
const measureSql = (name, alias = "") => {
|
|
3778
|
+
const column = (name) => `${alias}${name}`;
|
|
3779
|
+
if (name === "count") return "CAST(count(*) AS DOUBLE)";
|
|
3780
|
+
if (name === "errorCount") return `CAST(count_if(${column("isError")}) AS DOUBLE)`;
|
|
3781
|
+
if (name === "errorRate") return `CAST(count_if(${column("isError")}) AS DOUBLE) / count(*)`;
|
|
3782
|
+
if (name === "duration.avg") return `avg(${column("durationMs")})`;
|
|
3783
|
+
if (name === "duration.min") return `min(${column("durationMs")})`;
|
|
3784
|
+
if (name === "duration.max") return `max(${column("durationMs")})`;
|
|
3785
|
+
const percentile = PERCENTILES[name];
|
|
3786
|
+
if (percentile !== void 0) return `quantile_cont(${column("durationMs")}, ${percentile})`;
|
|
3787
|
+
const measure = plan.measures.find((candidate) => candidate.type === "countDistinct" && candidate.name === name);
|
|
3788
|
+
const distinctColumn = measure && distinctColumns.get(measure.field);
|
|
3789
|
+
if (distinctColumn === void 0) throw new Error(`Unsupported trusted trace-aggregate measure: ${name}`);
|
|
3790
|
+
return `CAST(count(DISTINCT ${alias}${distinctColumn}) AS DOUBLE)`;
|
|
3791
|
+
};
|
|
3792
|
+
const havingValues = [];
|
|
3793
|
+
const compileHaving = (predicate) => {
|
|
3794
|
+
if (predicate.type === "boolean") return predicate.args.map((arg) => `(${compileHaving(arg)})`).join(predicate.operator === "and" ? " AND " : " OR ");
|
|
3795
|
+
if (predicate.type === "not") return `NOT (${compileHaving(predicate.arg)})`;
|
|
3796
|
+
const measure = measureSql(predicate.measure);
|
|
3797
|
+
if (predicate.type === "membership") {
|
|
3798
|
+
if (predicate.values.length === 0) return predicate.operator === "in" ? "FALSE" : "TRUE";
|
|
3799
|
+
havingValues.push(...predicate.values);
|
|
3800
|
+
const list = predicate.values.map(() => "CAST(? AS DOUBLE)").join(", ");
|
|
3801
|
+
return `${measure} ${predicate.operator === "in" ? "IN" : "NOT IN"} (${list})`;
|
|
3802
|
+
}
|
|
3803
|
+
const operator = COMPARISON_SQL[predicate.operator];
|
|
3804
|
+
if (operator === void 0) throw new Error(`Unsupported trusted trace-aggregate operator: ${predicate.operator}`);
|
|
3805
|
+
havingValues.push(predicate.value);
|
|
3806
|
+
return `${measure} ${operator} CAST(? AS DOUBLE)`;
|
|
3807
|
+
};
|
|
3808
|
+
const havingSql = ["count(*) > 0", ...plan.having ? [`(${compileHaving(plan.having)})`] : []].join(" AND ");
|
|
3809
|
+
const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
|
|
3810
|
+
let orderTarget;
|
|
3811
|
+
if (plan.orderBy.target === "measure") orderTarget = measureSql(plan.orderBy.measure);
|
|
3812
|
+
else {
|
|
3813
|
+
const index = plan.dimensions.indexOf(plan.orderBy.dimension);
|
|
3814
|
+
if (index === -1) throw new Error(`Unsupported trusted trace-aggregate order dimension: ${plan.orderBy.dimension}`);
|
|
3815
|
+
orderTarget = dimensionColumns[index];
|
|
3816
|
+
}
|
|
3817
|
+
const orderSql = [`${orderTarget} ${direction} NULLS LAST`, ...dimensionColumns.map((column) => `${column} ASC NULLS LAST`)].join(", ");
|
|
3818
|
+
const groupSql = dimensionColumns.length > 0 ? `\nGROUP BY ${dimensionColumns.join(", ")}` : "";
|
|
3819
|
+
if (plan.interval === void 0) {
|
|
3820
|
+
const projected = [...dimensionColumns, ...plan.measures.map((measure, index) => `${measureSql(measure.name)} AS m${index}`)];
|
|
3821
|
+
values.push(...havingValues, plan.limit + 1);
|
|
3822
|
+
return {
|
|
3823
|
+
sql: `WITH ${ctes.join(",\n ")}
|
|
3824
|
+
SELECT ${projected.join(", ")}
|
|
3825
|
+
FROM facts${groupSql}
|
|
3826
|
+
HAVING ${havingSql}
|
|
3827
|
+
ORDER BY ${orderSql}
|
|
3828
|
+
LIMIT ?`,
|
|
3829
|
+
values
|
|
3830
|
+
};
|
|
3831
|
+
}
|
|
3832
|
+
ctes.push(`ranked AS (
|
|
3833
|
+
SELECT ${[...dimensionColumns, `row_number() OVER (ORDER BY ${orderSql}) AS rank`].join(", ")}
|
|
3834
|
+
FROM facts${groupSql}
|
|
3835
|
+
HAVING ${havingSql}
|
|
3836
|
+
ORDER BY ${orderSql}
|
|
3837
|
+
LIMIT ?
|
|
3838
|
+
)`);
|
|
3839
|
+
values.push(...havingValues, plan.limit + 1);
|
|
3840
|
+
const joinSql = dimensionColumns.length > 0 ? dimensionColumns.map((column) => `f.${column} IS NOT DISTINCT FROM g.${column}`).join(" AND ") : "TRUE";
|
|
3841
|
+
const projected = [
|
|
3842
|
+
...dimensionColumns.map((column) => `f.${column}`),
|
|
3843
|
+
"f.bucket",
|
|
3844
|
+
...plan.measures.map((measure, index) => `${measureSql(measure.name, "f.")} AS m${index}`),
|
|
3845
|
+
`(SELECT count(*) FROM ranked) > ? AS truncated`
|
|
3846
|
+
];
|
|
3847
|
+
values.push(plan.limit, plan.limit);
|
|
3848
|
+
return {
|
|
3849
|
+
sql: `WITH ${ctes.join(",\n ")}
|
|
3850
|
+
SELECT ${projected.join(", ")}
|
|
3851
|
+
FROM facts f
|
|
3852
|
+
JOIN ranked g ON ${joinSql}
|
|
3853
|
+
WHERE g.rank <= ?
|
|
3854
|
+
GROUP BY ${[
|
|
3855
|
+
"g.rank",
|
|
3856
|
+
...dimensionColumns.map((column) => `f.${column}`),
|
|
3857
|
+
"f.bucket"
|
|
3858
|
+
].join(", ")}
|
|
3859
|
+
ORDER BY g.rank ASC, f.bucket ASC`,
|
|
3860
|
+
values
|
|
3861
|
+
};
|
|
3862
|
+
}
|
|
3863
|
+
async function aggregateTraces(db, plan) {
|
|
3864
|
+
const compiled = compileDuckDBTraceAggregate(plan);
|
|
3865
|
+
let result;
|
|
3866
|
+
try {
|
|
3867
|
+
result = await db.query(compiled.sql, compiled.values);
|
|
3868
|
+
} catch (error) {
|
|
3869
|
+
if (isDuckDBResourceLimit(error)) throw new _mastra_core_storage.TraceQueryResourceLimitError();
|
|
3870
|
+
throw error;
|
|
3871
|
+
}
|
|
3872
|
+
let truncated;
|
|
3873
|
+
let resultRows;
|
|
3874
|
+
if (plan.interval === void 0) {
|
|
3875
|
+
truncated = result.length > plan.limit;
|
|
3876
|
+
resultRows = result.slice(0, plan.limit);
|
|
3877
|
+
} else {
|
|
3878
|
+
truncated = result[0]?.truncated === true;
|
|
3879
|
+
resultRows = result;
|
|
3880
|
+
}
|
|
3881
|
+
return {
|
|
3882
|
+
rows: resultRows.map((row) => {
|
|
3883
|
+
const shaped = { measures: Object.fromEntries(plan.measures.map((measure, index) => [measure.name, Number(row[`m${index}`])])) };
|
|
3884
|
+
if (plan.dimensions.length > 0) shaped.dimensions = Object.fromEntries(plan.dimensions.map((dimension, index) => {
|
|
3885
|
+
const value = row[`d${index}`];
|
|
3886
|
+
return [dimension, value == null ? null : String(value)];
|
|
3887
|
+
}));
|
|
3888
|
+
if (plan.interval !== void 0) shaped.bucket = asIsoTimestamp(row.bucket);
|
|
3889
|
+
return shaped;
|
|
3890
|
+
}),
|
|
3891
|
+
truncated
|
|
3892
|
+
};
|
|
3893
|
+
}
|
|
3894
|
+
//#endregion
|
|
3646
3895
|
//#region src/storage/domains/observability/tracing.ts
|
|
3647
3896
|
const COLUMNS_SQL = [
|
|
3648
3897
|
"eventType",
|
|
@@ -4970,6 +5219,7 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends _mastr
|
|
|
4970
5219
|
"tag-discovery",
|
|
4971
5220
|
"metric-discovery",
|
|
4972
5221
|
"trace-query",
|
|
5222
|
+
"trace-aggregate",
|
|
4973
5223
|
"trace-query-root-duration",
|
|
4974
5224
|
"trace-query-discovery",
|
|
4975
5225
|
"thread-query",
|
|
@@ -4988,6 +5238,7 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends _mastr
|
|
|
4988
5238
|
"metric-discovery",
|
|
4989
5239
|
"delta-polling",
|
|
4990
5240
|
"trace-query",
|
|
5241
|
+
"trace-aggregate",
|
|
4991
5242
|
"trace-query-root-duration",
|
|
4992
5243
|
"trace-query-discovery",
|
|
4993
5244
|
"thread-query",
|
|
@@ -5026,6 +5277,9 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends _mastr
|
|
|
5026
5277
|
async queryTraces(plan) {
|
|
5027
5278
|
return queryTraces(this.db, plan);
|
|
5028
5279
|
}
|
|
5280
|
+
async aggregateTraces(plan) {
|
|
5281
|
+
return aggregateTraces(this.db, plan);
|
|
5282
|
+
}
|
|
5029
5283
|
async getTraceQueryObservedFields(plan) {
|
|
5030
5284
|
return getTraceQueryObservedFields(this.db, plan);
|
|
5031
5285
|
}
|
|
@@ -5148,4 +5402,4 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends _mastr
|
|
|
5148
5402
|
//#endregion
|
|
5149
5403
|
exports.ObservabilityStorageDuckDB = ObservabilityStorageDuckDB;
|
|
5150
5404
|
|
|
5151
|
-
//# sourceMappingURL=observability-
|
|
5405
|
+
//# sourceMappingURL=observability-BCEHa8GU.cjs.map
|