@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.
@@ -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: `NULLIF(trim(CASE WHEN json_type(r.metadata, ?) = 'VARCHAR' THEN json_extract_string(r.metadata, ?) END), '')`,
3004
+ sql: TRACE_METADATA_VALUE_SQL,
2998
3005
  parameterType: "scalar"
2999
3006
  };
3000
- fieldValues = [path, path];
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
- function compileDuckDBTraceScope(relatedCollections, scope) {
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 values = [...tenantValues];
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
- function compileDuckDBTraceQuery(plan) {
3283
- const { ctes, values: scopeValues } = compileDuckDBTraceScope(collectRelatedCollections(plan.where), plan.scope);
3284
- const values = [
3285
- plan.timeRange.from,
3286
- plan.timeRange.to,
3287
- ...scopeValues
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
- conditions.push(`(${predicate.sql})`);
3350
+ predicateSql = `(${predicate.sql})`;
3297
3351
  values.push(...predicate.values);
3298
3352
  }
3299
3353
  ctes.push(`candidates AS (
3300
- SELECT ${TRACE_SELECT}${plan.paginationMode === "delta" ? ", r.cursorId AS deltaWatermark" : ""}
3354
+ SELECT ${columns}
3301
3355
  FROM root_scope r
3302
- WHERE ${conditions.slice(3).join("\n AND ") || "TRUE"}
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 root_events),
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 root_events) AS streamHead" : ""}
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: scopeValues } = compileDuckDBTraceScope(relatedCollections, plan.scope);
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: scopeValues } = compileDuckDBTraceScope(/* @__PURE__ */ new Set(), plan.scope);
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: scopeValues } = compileDuckDBTraceScope(discoveryCollections(plan.predicateScope), plan.scope);
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-DlmDRPW3.cjs.map
5405
+ //# sourceMappingURL=observability-BCEHa8GU.cjs.map