@mastra/pg 1.29.0 → 1.30.0-alpha.1

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/index.js CHANGED
@@ -17484,6 +17484,16 @@ function fieldSql(registry, field) {
17484
17484
  if (sql === void 0) throw new Error(`Unsupported trusted trace-query field: ${field}`);
17485
17485
  return sql;
17486
17486
  }
17487
+ /**
17488
+ * Value of trace-root `metadata.<key>` (alias `r`) as filtering, discovery, and aggregate grouping
17489
+ * all see it: the normalized search value, falling back to the trimmed raw string.
17490
+ */
17491
+ function traceMetadataValueSql(keyParameter) {
17492
+ return `COALESCE(
17493
+ CASE WHEN jsonb_typeof(r."metadataSearch" -> ${keyParameter}) = 'string' THEN r."metadataSearch" ->> ${keyParameter} END,
17494
+ CASE WHEN jsonb_typeof(r."metadataRaw" -> ${keyParameter}) = 'string' THEN NULLIF(btrim(r."metadataRaw" ->> ${keyParameter}), '') END
17495
+ )`;
17496
+ }
17487
17497
  function isMetadataField(field) {
17488
17498
  return field.startsWith("metadata.");
17489
17499
  }
@@ -17513,11 +17523,7 @@ function compileScalarPredicate(predicate, registry, parameterOffset, allowMetad
17513
17523
  let fieldValues = [];
17514
17524
  if (isMetadataField(predicate.field)) {
17515
17525
  if (!allowMetadata) throw new Error(`Unsupported trusted trace-query field: ${predicate.field}`);
17516
- const keyParameter = `$${parameterOffset++}`;
17517
- field = `COALESCE(
17518
- CASE WHEN jsonb_typeof(r."metadataSearch" -> ${keyParameter}) = 'string' THEN r."metadataSearch" ->> ${keyParameter} END,
17519
- CASE WHEN jsonb_typeof(r."metadataRaw" -> ${keyParameter}) = 'string' THEN NULLIF(btrim(r."metadataRaw" ->> ${keyParameter}), '') END
17520
- )`;
17526
+ field = traceMetadataValueSql(`$${parameterOffset++}`);
17521
17527
  fieldValues = [predicate.field.slice(9)];
17522
17528
  } else field = fieldSql(registry, predicate.field);
17523
17529
  if (predicate.type === "presence") return {
@@ -17637,6 +17643,54 @@ function collectThreadRelationCollections(predicate, collections) {
17637
17643
  else collectThreadRelationCollections(predicate.arg, collections);
17638
17644
  return collections;
17639
17645
  }
17646
+ const ROOT_PUSHDOWN_FIELDS = /* @__PURE__ */ new Set([
17647
+ "traceId",
17648
+ "threadId",
17649
+ "resourceId",
17650
+ "runId",
17651
+ "sessionId",
17652
+ "userId",
17653
+ "organizationId",
17654
+ "startedAt",
17655
+ "endedAt",
17656
+ "entityName",
17657
+ "entityType",
17658
+ "environment"
17659
+ ]);
17660
+ function isRootPushdownPredicate(predicate) {
17661
+ if (predicate.type === "relation") return false;
17662
+ if (predicate.type === "boolean") return predicate.args.every(isRootPushdownPredicate);
17663
+ if (predicate.type === "not") return isRootPushdownPredicate(predicate.arg);
17664
+ return ROOT_PUSHDOWN_FIELDS.has(predicate.field);
17665
+ }
17666
+ /**
17667
+ * Splits `where` into top-level AND conjuncts that can filter `root_scope` directly and the rest,
17668
+ * which is applied in `candidates`. The latest-root check compares against the unfiltered span
17669
+ * table, so filtering a root earlier cannot change which root is current, but it stops long windows
17670
+ * from materializing every root (and running the latest-root check on it) before filtering.
17671
+ */
17672
+ function splitRootPredicate(predicate) {
17673
+ if (!predicate) return {
17674
+ root: [],
17675
+ rest: []
17676
+ };
17677
+ const conjuncts = predicate.type === "boolean" && predicate.operator === "and" ? predicate.args : [predicate];
17678
+ const root = [];
17679
+ const rest = [];
17680
+ for (const conjunct of conjuncts) (isRootPushdownPredicate(conjunct) ? root : rest).push(conjunct);
17681
+ return {
17682
+ root,
17683
+ rest
17684
+ };
17685
+ }
17686
+ function compileConjunction(predicates, values) {
17687
+ if (predicates.length === 0) return "TRUE";
17688
+ return predicates.map((predicate) => {
17689
+ const compiled = compilePredicate(predicate, values.length + 1);
17690
+ values.push(...compiled.values);
17691
+ return `(${compiled.sql})`;
17692
+ }).join(" AND ");
17693
+ }
17640
17694
  function compilePredicate(predicate, parameterOffset) {
17641
17695
  if (predicate.type === "relation") {
17642
17696
  const compiled = predicate.collection === "feedback" ? compileFeedbackScalarPredicate(predicate.predicate, parameterOffset) : compileScalarPredicate(predicate.predicate, predicate.collection === "spans" ? SPAN_FIELDS : SCORE_FIELDS, parameterOffset);
@@ -17701,7 +17755,7 @@ function compileThreadPredicate(predicate, parameterOffset) {
17701
17755
  values: compiled.values
17702
17756
  };
17703
17757
  }
17704
- function compilePostgresTraceScope(schema, selection, relationCollections, scope, deltaWindow) {
17758
+ function compilePostgresTraceScope(schema, selection, relationCollections, scope, deltaWindow, rootPredicates = []) {
17705
17759
  const spanTable = qualifiedTable(schema, TABLE_SPAN_EVENTS);
17706
17760
  const scoreTable = qualifiedTable(schema, TABLE_SCORE_EVENTS);
17707
17761
  const feedbackTable = qualifiedTable(schema, TABLE_FEEDBACK_EVENTS);
@@ -17721,10 +17775,7 @@ function compilePostgresTraceScope(schema, selection, relationCollections, scope
17721
17775
  }
17722
17776
  }
17723
17777
  const scopeSql = (alias) => scopeConditions.map((condition) => `\n AND ${alias}.${condition}`).join("");
17724
- const ctes = [`root_scope AS MATERIALIZED (
17725
- SELECT *
17726
- FROM ${spanTable} r
17727
- WHERE ${[
17778
+ const rootConditions = [
17728
17779
  `r."parentSpanId" IS NULL`,
17729
17780
  latestRootPredicate$1(spanTable),
17730
17781
  `NOT r."isPending"`,
@@ -17733,7 +17784,12 @@ function compilePostgresTraceScope(schema, selection, relationCollections, scope
17733
17784
  `r."startedAt" < $2`,
17734
17785
  ...deltaConditions,
17735
17786
  ...scopeConditions.map((condition) => `r.${condition}`)
17736
- ].join("\n AND ")}
17787
+ ];
17788
+ if (rootPredicates.length > 0) rootConditions.push(compileConjunction(rootPredicates, values));
17789
+ const ctes = [`root_scope AS MATERIALIZED (
17790
+ SELECT *
17791
+ FROM ${spanTable} r
17792
+ WHERE ${rootConditions.join("\n AND ")}
17737
17793
  )`];
17738
17794
  if (relationCollections.has("spans")) ctes.push(`current_spans AS MATERIALIZED (
17739
17795
  SELECT
@@ -17806,8 +17862,27 @@ function compilePostgresTraceScope(schema, selection, relationCollections, scope
17806
17862
  values
17807
17863
  };
17808
17864
  }
17865
+ /**
17866
+ * Builds the CTE chain ending in `candidates`: the completed, current trace roots in the
17867
+ * selection's time range and tenant scope that match its `where` predicate. Trace queries and
17868
+ * trace aggregates both select from this CTE so they always see the same population.
17869
+ */
17870
+ function compilePostgresTraceCandidates(schema, selection, columns, deltaWindow) {
17871
+ const relationCollections = collectRelationCollections(selection.where);
17872
+ const { root, rest } = splitRootPredicate(selection.where);
17873
+ const { ctes, values } = compilePostgresTraceScope(schema, selection, relationCollections, selection.scope, deltaWindow, root);
17874
+ const predicateSql = compileConjunction(rest, values);
17875
+ ctes.push(`candidates AS (
17876
+ SELECT ${columns}
17877
+ FROM root_scope r
17878
+ WHERE ${predicateSql}
17879
+ )`);
17880
+ return {
17881
+ ctes,
17882
+ values
17883
+ };
17884
+ }
17809
17885
  function compilePostgresTraceQuery(schema, plan, mode = "data", safeHorizon) {
17810
- const relationCollections = collectRelationCollections(plan.where);
17811
17886
  let deltaWindow;
17812
17887
  if (plan.paginationMode === "delta") {
17813
17888
  const watermark = coreStorage.getTraceQueryDeltaWatermark(plan, "pg");
@@ -17817,18 +17892,7 @@ function compilePostgresTraceQuery(schema, plan, mode = "data", safeHorizon) {
17817
17892
  safeHorizon
17818
17893
  };
17819
17894
  }
17820
- const { ctes, values } = compilePostgresTraceScope(schema, plan, relationCollections, plan.scope, deltaWindow);
17821
- let predicateSql = "TRUE";
17822
- if (plan.where) {
17823
- const predicate = compilePredicate(plan.where, values.length + 1);
17824
- predicateSql = predicate.sql;
17825
- values.push(...predicate.values);
17826
- }
17827
- ctes.push(`candidates AS (
17828
- SELECT ${TRACE_SELECT}${plan.paginationMode === "delta" ? ", r.\"xactId\", r.\"cursorId\"" : ""}
17829
- FROM root_scope r
17830
- WHERE ${predicateSql}
17831
- )`);
17895
+ const { ctes, values } = compilePostgresTraceCandidates(schema, plan, `${TRACE_SELECT}${plan.paginationMode === "delta" ? ", r.\"xactId\", r.\"cursorId\"" : ""}`, deltaWindow);
17832
17896
  const candidates = `WITH ${ctes.join(",\n")}`;
17833
17897
  if (plan.paginationMode === "page" && mode === "count") return {
17834
17898
  text: `${candidates}
@@ -17981,11 +18045,7 @@ function compilePostgresTraceQueryValues(schema, plan) {
17981
18045
  let extracted;
17982
18046
  if (plan.predicateScope === "trace" && plan.path === "tags") extracted = `SELECT value FROM (SELECT DISTINCT r."traceId", UNNEST(r."tags") AS value FROM root_scope r) t`;
17983
18047
  else if (plan.predicateScope === "trace" && plan.path.startsWith("metadata.")) {
17984
- const keyParameter = `$${values.length + 1}`;
17985
- extracted = `SELECT COALESCE(
17986
- CASE WHEN jsonb_typeof(r."metadataSearch" -> ${keyParameter}) = 'string' THEN r."metadataSearch" ->> ${keyParameter} END,
17987
- CASE WHEN jsonb_typeof(r."metadataRaw" -> ${keyParameter}) = 'string' THEN NULLIF(btrim(r."metadataRaw" ->> ${keyParameter}), '') END
17988
- )::text AS value FROM root_scope r`;
18048
+ extracted = `SELECT ${traceMetadataValueSql(`$${values.length + 1}`)}::text AS value FROM root_scope r`;
17989
18049
  values.push(plan.path.slice(9));
17990
18050
  } else extracted = `SELECT ${fieldSql(discoveryRegistry(plan.predicateScope), plan.path)}::text AS value FROM ${discoverySource(plan.predicateScope)}`;
17991
18051
  const searchParameter = values.length + 1;
@@ -18197,6 +18257,187 @@ async function queryThreads(client, schema, plan, timeoutMs) {
18197
18257
  });
18198
18258
  }
18199
18259
  //#endregion
18260
+ //#region src/storage/domains/observability/v-next/trace-aggregate.ts
18261
+ /** Trace-root column expressions (alias `r`) for each groupable dimension; same values `where` sees. */
18262
+ const DIMENSION_SQL = {
18263
+ entityType: "r.\"entityType\"",
18264
+ entityName: "r.\"entityName\"",
18265
+ environment: "r.\"environment\"",
18266
+ status: TRACE_STATUS_SQL,
18267
+ serviceName: "r.\"serviceName\"",
18268
+ executionSource: "r.\"executionSource\"",
18269
+ threadId: "r.\"threadId\"",
18270
+ resourceId: "r.\"resourceId\"",
18271
+ userId: "r.\"userId\"",
18272
+ sessionId: "r.\"sessionId\"",
18273
+ organizationId: "r.\"organizationId\"",
18274
+ experimentId: "r.\"experimentId\""
18275
+ };
18276
+ const PERCENTILES = {
18277
+ "duration.p50": .5,
18278
+ "duration.p90": .9,
18279
+ "duration.p95": .95,
18280
+ "duration.p99": .99
18281
+ };
18282
+ const COMPARISON_SQL = {
18283
+ eq: "=",
18284
+ ne: "<>",
18285
+ lt: "<",
18286
+ lte: "<=",
18287
+ gt: ">",
18288
+ gte: ">="
18289
+ };
18290
+ /**
18291
+ * Compiles a trusted aggregate plan. The plan's caps are enforced by the planner and trusted here.
18292
+ *
18293
+ * Every candidate root is first reduced to a `facts` row (dimension values, duration, error flag,
18294
+ * countDistinct inputs, bucket) so grouping, measures, and joins only reference plain columns.
18295
+ * Without `interval`, groups are filtered, ordered, and limited in one grouped select. With
18296
+ * `interval`, a `ranked` CTE ranks groups on whole-window measures, then only the top `limit`
18297
+ * groups are expanded into per-bucket rows.
18298
+ */
18299
+ function compilePostgresTraceAggregate(schema, plan) {
18300
+ const { ctes, values } = compilePostgresTraceCandidates(schema, plan, "r.*");
18301
+ const param = (value) => {
18302
+ values.push(value);
18303
+ return `$${values.length}`;
18304
+ };
18305
+ const dimensionSql = (field) => {
18306
+ if (field.startsWith("metadata.")) return traceMetadataValueSql(param(field.slice(9)));
18307
+ const sql = Object.hasOwn(DIMENSION_SQL, field) ? DIMENSION_SQL[field] : void 0;
18308
+ if (sql === void 0) throw new Error(`Unsupported trusted trace-aggregate field: ${field}`);
18309
+ return sql;
18310
+ };
18311
+ const distinctSql = (field) => field === "traceId" ? "r.\"traceId\"" : dimensionSql(field);
18312
+ const dimensionColumns = plan.dimensions.map((_, index) => `"d${index}"`);
18313
+ const distinctColumns = /* @__PURE__ */ new Map();
18314
+ const factColumns = plan.dimensions.map((dimension, index) => `(${dimensionSql(dimension)})::text AS "d${index}"`);
18315
+ for (const measure of plan.measures) {
18316
+ if (measure.type !== "countDistinct" || distinctColumns.has(measure.field)) continue;
18317
+ const column = `"cd${distinctColumns.size}"`;
18318
+ distinctColumns.set(measure.field, column);
18319
+ factColumns.push(`(${distinctSql(measure.field)})::text AS ${column}`);
18320
+ }
18321
+ factColumns.push(`(${durationMsSql("r.\"startedAt\"", "r.\"endedAt\"")})::float8 AS "durationMs"`, `r."error" IS NOT NULL AS "isError"`);
18322
+ if (plan.interval !== void 0) {
18323
+ const intervalMs = coreStorage.TRACE_AGGREGATE_INTERVAL_MS[plan.interval];
18324
+ if (intervalMs === void 0) throw new Error(`Unsupported trusted trace-aggregate interval: ${plan.interval}`);
18325
+ factColumns.push(`to_timestamp(floor(EXTRACT(EPOCH FROM r."startedAt") * 1000 / ${intervalMs}) * ${intervalMs} / 1000.0) AS "bucket"`);
18326
+ }
18327
+ ctes.push(`facts AS (
18328
+ SELECT ${factColumns.join(",\n ")}
18329
+ FROM candidates r
18330
+ )`);
18331
+ const measureSql = (name, alias = "") => {
18332
+ const column = (name) => `${alias}"${name}"`;
18333
+ if (name === "count") return "COUNT(*)::float8";
18334
+ if (name === "errorCount") return `(COUNT(*) FILTER (WHERE ${column("isError")}))::float8`;
18335
+ if (name === "errorRate") return `(COUNT(*) FILTER (WHERE ${column("isError")}))::float8 / COUNT(*)`;
18336
+ if (name === "duration.avg") return `AVG(${column("durationMs")})`;
18337
+ if (name === "duration.min") return `MIN(${column("durationMs")})`;
18338
+ if (name === "duration.max") return `MAX(${column("durationMs")})`;
18339
+ const percentile = PERCENTILES[name];
18340
+ if (percentile !== void 0) return `percentile_cont(${percentile}) WITHIN GROUP (ORDER BY ${column("durationMs")})`;
18341
+ const measure = plan.measures.find((candidate) => candidate.type === "countDistinct" && candidate.name === name);
18342
+ const distinctColumn = measure && distinctColumns.get(measure.field);
18343
+ if (distinctColumn === void 0) throw new Error(`Unsupported trusted trace-aggregate measure: ${name}`);
18344
+ return `(COUNT(DISTINCT ${alias}${distinctColumn}))::float8`;
18345
+ };
18346
+ const compileHaving = (predicate) => {
18347
+ if (predicate.type === "boolean") return predicate.args.map((arg) => `(${compileHaving(arg)})`).join(predicate.operator === "and" ? " AND " : " OR ");
18348
+ if (predicate.type === "not") return `NOT (${compileHaving(predicate.arg)})`;
18349
+ const measure = measureSql(predicate.measure);
18350
+ if (predicate.type === "membership") {
18351
+ if (predicate.values.length === 0) return predicate.operator === "in" ? "FALSE" : "TRUE";
18352
+ const list = predicate.values.map((value) => `${param(value)}::float8`).join(", ");
18353
+ return `${measure} ${predicate.operator === "in" ? "IN" : "NOT IN"} (${list})`;
18354
+ }
18355
+ const operator = COMPARISON_SQL[predicate.operator];
18356
+ if (operator === void 0) throw new Error(`Unsupported trusted trace-aggregate operator: ${predicate.operator}`);
18357
+ return `${measure} ${operator} ${param(predicate.value)}::float8`;
18358
+ };
18359
+ const havingSql = ["COUNT(*) > 0", ...plan.having ? [`(${compileHaving(plan.having)})`] : []].join(" AND ");
18360
+ const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
18361
+ let orderTarget;
18362
+ if (plan.orderBy.target === "measure") orderTarget = measureSql(plan.orderBy.measure);
18363
+ else {
18364
+ const index = plan.dimensions.indexOf(plan.orderBy.dimension);
18365
+ if (index === -1) throw new Error(`Unsupported trusted trace-aggregate order dimension: ${plan.orderBy.dimension}`);
18366
+ orderTarget = `${dimensionColumns[index]} COLLATE "C"`;
18367
+ }
18368
+ const orderSql = [`${orderTarget} ${direction} NULLS LAST`, ...dimensionColumns.map((column) => `${column} COLLATE "C" ASC NULLS LAST`)].join(", ");
18369
+ const groupSql = dimensionColumns.length > 0 ? dimensionColumns.join(", ") : "()";
18370
+ const limitParameter = param(plan.limit + 1);
18371
+ if (plan.interval === void 0) {
18372
+ const projected = [...dimensionColumns, ...plan.measures.map((measure, index) => `${measureSql(measure.name)} AS "m${index}"`)];
18373
+ return {
18374
+ text: `WITH ${ctes.join(",\n")}
18375
+ SELECT ${projected.join(", ")}
18376
+ FROM facts
18377
+ GROUP BY ${groupSql}
18378
+ HAVING ${havingSql}
18379
+ ORDER BY ${orderSql}
18380
+ LIMIT ${limitParameter}`,
18381
+ values
18382
+ };
18383
+ }
18384
+ const maxRankParameter = param(plan.limit);
18385
+ ctes.push(`ranked AS (
18386
+ SELECT ${[...dimensionColumns, `row_number() OVER (ORDER BY ${orderSql}) AS "rank"`].join(", ")}
18387
+ FROM facts
18388
+ GROUP BY ${groupSql}
18389
+ HAVING ${havingSql}
18390
+ ORDER BY ${orderSql}
18391
+ LIMIT ${limitParameter}
18392
+ )`);
18393
+ const joinSql = dimensionColumns.length > 0 ? dimensionColumns.map((column) => `COALESCE(f.${column}, '') = COALESCE(g.${column}, '') AND (f.${column} IS NULL) = (g.${column} IS NULL)`).join(" AND ") : "TRUE";
18394
+ const projected = [
18395
+ ...dimensionColumns.map((column) => `f.${column}`),
18396
+ "f.\"bucket\"",
18397
+ ...plan.measures.map((measure, index) => `${measureSql(measure.name, "f.")} AS "m${index}"`),
18398
+ `(SELECT COUNT(*) FROM ranked) > ${maxRankParameter} AS "truncated"`
18399
+ ];
18400
+ return {
18401
+ text: `WITH ${ctes.join(",\n")}
18402
+ SELECT ${projected.join(", ")}
18403
+ FROM facts f
18404
+ JOIN ranked g ON ${joinSql}
18405
+ WHERE g."rank" <= ${maxRankParameter}
18406
+ GROUP BY ${[
18407
+ "g.\"rank\"",
18408
+ ...dimensionColumns.map((column) => `f.${column}`),
18409
+ "f.\"bucket\""
18410
+ ].join(", ")}
18411
+ ORDER BY g."rank" ASC, f."bucket" ASC`,
18412
+ values
18413
+ };
18414
+ }
18415
+ function toIsoTimestamp(value) {
18416
+ return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
18417
+ }
18418
+ async function aggregateTraces(client, schema, plan, timeoutMs) {
18419
+ const compiled = compilePostgresTraceAggregate(schema, plan);
18420
+ const result = await runWithPostgresTraceQueryTimeout(client, timeoutMs, (transaction) => transaction.any(compiled.text, compiled.values));
18421
+ let truncated;
18422
+ let resultRows;
18423
+ if (plan.interval === void 0) {
18424
+ truncated = result.length > plan.limit;
18425
+ resultRows = result.slice(0, plan.limit);
18426
+ } else {
18427
+ truncated = result[0]?.truncated === true;
18428
+ resultRows = result;
18429
+ }
18430
+ return {
18431
+ rows: resultRows.map((row) => {
18432
+ const shaped = { measures: Object.fromEntries(plan.measures.map((measure, index) => [measure.name, Number(row[`m${index}`])])) };
18433
+ if (plan.dimensions.length > 0) shaped.dimensions = Object.fromEntries(plan.dimensions.map((dimension, index) => [dimension, row[`d${index}`] ?? null]));
18434
+ if (plan.interval !== void 0) shaped.bucket = toIsoTimestamp(row.bucket);
18435
+ return shaped;
18436
+ }),
18437
+ truncated
18438
+ };
18439
+ }
18440
+ //#endregion
18200
18441
  //#region src/storage/domains/observability/v-next/traces.ts
18201
18442
  /**
18202
18443
  * Trace / branch list reads for the v-next Postgres observability domain.
@@ -19098,6 +19339,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
19098
19339
  "tag-discovery",
19099
19340
  "metric-discovery",
19100
19341
  "trace-query",
19342
+ "trace-aggregate",
19101
19343
  "trace-query-root-duration",
19102
19344
  "trace-query-discovery",
19103
19345
  "thread-query",
@@ -19116,6 +19358,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
19116
19358
  "metric-discovery",
19117
19359
  "delta-polling",
19118
19360
  "trace-query",
19361
+ "trace-aggregate",
19119
19362
  "trace-query-root-duration",
19120
19363
  "trace-query-discovery",
19121
19364
  "thread-query",
@@ -19167,6 +19410,9 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
19167
19410
  async queryTraces(plan) {
19168
19411
  return this.#run("QUERY_TRACES", () => queryTraces(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
19169
19412
  }
19413
+ async aggregateTraces(plan) {
19414
+ return this.#run("AGGREGATE_TRACES", () => aggregateTraces(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
19415
+ }
19170
19416
  async getTraceQueryObservedFields(plan) {
19171
19417
  return this.#run("GET_TRACE_QUERY_OBSERVED_FIELDS", () => getTraceQueryObservedFields(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
19172
19418
  }
@@ -20178,9 +20424,9 @@ var SchedulesPG = class SchedulesPG extends SchedulesStorage {
20178
20424
  if (!updated) throw new Error(`Schedule ${id} not found`);
20179
20425
  return updated;
20180
20426
  }
20181
- async updateScheduleNextFire(id, expectedNextFireAt, newNextFireAt, lastFireAt, lastRunId) {
20427
+ async updateScheduleNextFire(id, expectedNextFireAt, newNextFireAt, lastFireAt, lastRunId, newStatus) {
20182
20428
  return ((await this.#client.query(`UPDATE ${this.#table(TABLE_SCHEDULES)}
20183
- SET next_fire_at = $1, last_fire_at = $2, last_run_id = $3, updated_at = $4
20429
+ SET next_fire_at = $1, last_fire_at = $2, last_run_id = $3, updated_at = $4, status = COALESCE($8, status)
20184
20430
  WHERE id = $5 AND next_fire_at = $6 AND status = $7`, [
20185
20431
  newNextFireAt,
20186
20432
  lastFireAt,
@@ -20188,7 +20434,8 @@ var SchedulesPG = class SchedulesPG extends SchedulesStorage {
20188
20434
  Date.now(),
20189
20435
  id,
20190
20436
  expectedNextFireAt,
20191
- "active"
20437
+ "active",
20438
+ newStatus ?? null
20192
20439
  ])).rowCount ?? 0) > 0;
20193
20440
  }
20194
20441
  async deleteSchedule(id) {