@mastra/pg 1.31.2-alpha.0 → 1.31.2-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/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +406 -146
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +407 -147
- package/dist/index.js.map +1 -1
- package/dist/storage/db/sanitize-json.d.ts +2 -0
- package/dist/storage/db/sanitize-json.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/ddl.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/helpers.d.ts +7 -0
- package/dist/storage/domains/observability/v-next/helpers.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +2 -1
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/preview-compat.d.ts +47 -0
- package/dist/storage/domains/observability/v-next/preview-compat.d.ts.map +1 -0
- package/dist/storage/domains/observability/v-next/signal-schema.d.ts +12 -0
- package/dist/storage/domains/observability/v-next/signal-schema.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/span-query.d.ts +2 -0
- package/dist/storage/domains/observability/v-next/span-query.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/sql.d.ts +11 -0
- package/dist/storage/domains/observability/v-next/sql.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/traces.d.ts +3 -1
- package/dist/storage/domains/observability/v-next/traces.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -2472,6 +2472,10 @@ function repairJson(value) {
|
|
|
2472
2472
|
}
|
|
2473
2473
|
return value;
|
|
2474
2474
|
}
|
|
2475
|
+
/** Repair text for a PostgreSQL `text` column, which rejects NUL characters. */
|
|
2476
|
+
function toPgText(value) {
|
|
2477
|
+
return repair(value);
|
|
2478
|
+
}
|
|
2475
2479
|
/** Serialize JSON values without emitting Unicode sequences PostgreSQL rejects. */
|
|
2476
2480
|
function toPgJson(value) {
|
|
2477
2481
|
const json = JSON.stringify(value);
|
|
@@ -16075,6 +16079,21 @@ const SPAN_EVENT_COLUMNS = [
|
|
|
16075
16079
|
name: "requestContext",
|
|
16076
16080
|
type: "jsonb",
|
|
16077
16081
|
nullable: true
|
|
16082
|
+
},
|
|
16083
|
+
{
|
|
16084
|
+
name: "inputPreview",
|
|
16085
|
+
type: "text",
|
|
16086
|
+
nullable: true
|
|
16087
|
+
},
|
|
16088
|
+
{
|
|
16089
|
+
name: "outputPreview",
|
|
16090
|
+
type: "text",
|
|
16091
|
+
nullable: true
|
|
16092
|
+
},
|
|
16093
|
+
{
|
|
16094
|
+
name: "errorPreview",
|
|
16095
|
+
type: "text",
|
|
16096
|
+
nullable: true
|
|
16078
16097
|
}
|
|
16079
16098
|
];
|
|
16080
16099
|
const METRIC_EVENT_COLUMNS = [
|
|
@@ -16730,15 +16749,27 @@ function allTableDDL(schema, mode) {
|
|
|
16730
16749
|
* default does not rewrite the table, and the ALTER cascades to partitions.
|
|
16731
16750
|
*/
|
|
16732
16751
|
function additiveColumns(schema) {
|
|
16733
|
-
return [
|
|
16734
|
-
|
|
16735
|
-
|
|
16736
|
-
|
|
16737
|
-
|
|
16738
|
-
|
|
16739
|
-
|
|
16740
|
-
|
|
16741
|
-
|
|
16752
|
+
return [
|
|
16753
|
+
{
|
|
16754
|
+
table: TABLE_SPAN_EVENTS,
|
|
16755
|
+
column: "isPending",
|
|
16756
|
+
ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} ADD COLUMN IF NOT EXISTS "isPending" boolean NOT NULL DEFAULT false`
|
|
16757
|
+
},
|
|
16758
|
+
...[
|
|
16759
|
+
"inputPreview",
|
|
16760
|
+
"outputPreview",
|
|
16761
|
+
"errorPreview"
|
|
16762
|
+
].map((column) => ({
|
|
16763
|
+
table: TABLE_SPAN_EVENTS,
|
|
16764
|
+
column,
|
|
16765
|
+
ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} ADD COLUMN IF NOT EXISTS "${column}" text`
|
|
16766
|
+
})),
|
|
16767
|
+
{
|
|
16768
|
+
table: TABLE_FEEDBACK_EVENTS,
|
|
16769
|
+
column: "reviewStatus",
|
|
16770
|
+
ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_FEEDBACK_EVENTS)} ADD COLUMN IF NOT EXISTS "reviewStatus" text NOT NULL DEFAULT 'needs-review'`
|
|
16771
|
+
}
|
|
16772
|
+
];
|
|
16742
16773
|
}
|
|
16743
16774
|
/** Existence probe for an additive column. Params: schema, table, column. */
|
|
16744
16775
|
const columnExistsSQL = `SELECT 1 FROM information_schema.columns
|
|
@@ -17060,6 +17091,46 @@ function whereOrEmpty(acc) {
|
|
|
17060
17091
|
return acc.conditions.length ? `WHERE ${acc.conditions.join(" AND ")}` : "";
|
|
17061
17092
|
}
|
|
17062
17093
|
//#endregion
|
|
17094
|
+
//#region src/storage/domains/observability/v-next/preview-compat.ts
|
|
17095
|
+
const generator = _mastra_core_storage.buildStoredPreview;
|
|
17096
|
+
const canGeneratePreviews = typeof generator === "function";
|
|
17097
|
+
/** SQL detection only; preview formatting stays in TypeScript. Empty strings are intentional. */
|
|
17098
|
+
function missingPreviewSql(column) {
|
|
17099
|
+
return canGeneratePreviews ? `${column} IS NULL` : "FALSE";
|
|
17100
|
+
}
|
|
17101
|
+
function needsPreview(value) {
|
|
17102
|
+
return canGeneratePreviews && value == null;
|
|
17103
|
+
}
|
|
17104
|
+
function storedPreview(preview, value, serialized) {
|
|
17105
|
+
return canGeneratePreviews ? generator(preview, value, serialized) : preview ?? null;
|
|
17106
|
+
}
|
|
17107
|
+
function readPreview(preview, payload, jsonEncoded = false) {
|
|
17108
|
+
const supplied = preview;
|
|
17109
|
+
if (!canGeneratePreviews) return supplied ?? null;
|
|
17110
|
+
if (supplied != null) return supplied.trim() || null;
|
|
17111
|
+
let value = payload;
|
|
17112
|
+
if (needsPreview(preview) && jsonEncoded && typeof payload === "string") try {
|
|
17113
|
+
value = JSON.parse(payload);
|
|
17114
|
+
} catch {
|
|
17115
|
+
value = null;
|
|
17116
|
+
}
|
|
17117
|
+
return storedPreview(supplied, value, jsonEncoded && typeof payload === "string" ? payload : void 0) || null;
|
|
17118
|
+
}
|
|
17119
|
+
/** Older response builders may shorten previews or turn empty strings into null. */
|
|
17120
|
+
function buildSpanQueryResponse(...args) {
|
|
17121
|
+
if (canGeneratePreviews) return _mastra_core_storage.buildSpanQueryResponse(...args);
|
|
17122
|
+
const [plan, selected, payloads, metrics] = args;
|
|
17123
|
+
const response = _mastra_core_storage.buildSpanQueryResponse(plan, selected, [], metrics);
|
|
17124
|
+
const byIdentity = new Map(payloads.map((payload) => [_mastra_core_storage.spanQueryIdentityKey(payload), payload]));
|
|
17125
|
+
for (const span of response.spans) {
|
|
17126
|
+
const payload = byIdentity.get(_mastra_core_storage.spanQueryIdentityKey(span));
|
|
17127
|
+
span.inputPreview = payload?.inputPreview ?? null;
|
|
17128
|
+
span.outputPreview = payload?.outputPreview ?? null;
|
|
17129
|
+
span.errorPreview = payload?.errorPreview ?? null;
|
|
17130
|
+
}
|
|
17131
|
+
return response;
|
|
17132
|
+
}
|
|
17133
|
+
//#endregion
|
|
17063
17134
|
//#region src/storage/domains/observability/v-next/review-status.ts
|
|
17064
17135
|
const FEEDBACK_REVIEW_STATUSES = ["needs-review", "reviewed"];
|
|
17065
17136
|
function isFeedbackReviewStatus(value) {
|
|
@@ -17085,6 +17156,137 @@ function parseUpdateFeedbackReviewStatusArgs(args) {
|
|
|
17085
17156
|
};
|
|
17086
17157
|
}
|
|
17087
17158
|
//#endregion
|
|
17159
|
+
//#region src/storage/domains/observability/v-next/sql.ts
|
|
17160
|
+
/**
|
|
17161
|
+
* SQL helpers for the v-next Postgres observability domain.
|
|
17162
|
+
*
|
|
17163
|
+
* Provides a multi-row INSERT builder with a configurable conflict clause
|
|
17164
|
+
* (`ON CONFLICT DO NOTHING` by default, for retry idempotency), and explicit
|
|
17165
|
+
* jsonb / text[] casts so the pg driver doesn't have to guess column types.
|
|
17166
|
+
*/
|
|
17167
|
+
/**
|
|
17168
|
+
* Encode a JS value for a `$N::jsonb` cast. Always `JSON.stringify` so a
|
|
17169
|
+
* plain string like `"hello"` becomes `"hello"` (a valid JSON scalar) and
|
|
17170
|
+
* not the bare word `hello`, which Postgres rejects when cast to jsonb.
|
|
17171
|
+
*
|
|
17172
|
+
* The result is sanitized because Postgres rejects some sequences that are
|
|
17173
|
+
* legal in JSON: NUL (`\u0000`) fails with 22P05 and unpaired UTF-16
|
|
17174
|
+
* surrogates fail with 22P02. Inserts here are batched into a single
|
|
17175
|
+
* multi-row statement, so one bad value would otherwise reject the whole
|
|
17176
|
+
* batch of observability events.
|
|
17177
|
+
*/
|
|
17178
|
+
function encodeJsonb(value) {
|
|
17179
|
+
if (value === null || value === void 0) return null;
|
|
17180
|
+
if (value instanceof EncodedJsonb) return value.json;
|
|
17181
|
+
return toPgJson(value);
|
|
17182
|
+
}
|
|
17183
|
+
/**
|
|
17184
|
+
* A jsonb value the caller already serialized with `toPgJson`, so `buildInsert`
|
|
17185
|
+
* sends it as is instead of serializing it again. Span writes serialize their
|
|
17186
|
+
* payloads up front because the stored previews are built from that same text.
|
|
17187
|
+
*/
|
|
17188
|
+
var EncodedJsonb = class {
|
|
17189
|
+
json;
|
|
17190
|
+
constructor(json) {
|
|
17191
|
+
this.json = json;
|
|
17192
|
+
}
|
|
17193
|
+
};
|
|
17194
|
+
/** Serialize a value for a jsonb column once, for callers that also need the text. */
|
|
17195
|
+
function encodeJsonbValue(value) {
|
|
17196
|
+
if (value === null || value === void 0) return null;
|
|
17197
|
+
const json = toPgJson(value);
|
|
17198
|
+
return json === void 0 ? null : new EncodedJsonb(json);
|
|
17199
|
+
}
|
|
17200
|
+
/**
|
|
17201
|
+
* Build a multi-row INSERT with explicit column types and ON CONFLICT DO NOTHING.
|
|
17202
|
+
*
|
|
17203
|
+
* @param schema Schema name.
|
|
17204
|
+
* @param table Table name.
|
|
17205
|
+
* @param records Array of records (each is a column-name → value object).
|
|
17206
|
+
* All records must have identical key sets.
|
|
17207
|
+
* @returns { text, values } ready to pass to `client.query`.
|
|
17208
|
+
*/
|
|
17209
|
+
function buildInsert(schema, table, records, onConflict = "ON CONFLICT DO NOTHING") {
|
|
17210
|
+
if (records.length === 0) return null;
|
|
17211
|
+
const columns = Object.keys(records[0]).map((c) => (0, _mastra_core_utils.parseSqlIdentifier)(c, "column name"));
|
|
17212
|
+
const quotedColumns = columns.map((c) => `"${c}"`).join(", ");
|
|
17213
|
+
const values = [];
|
|
17214
|
+
const rowPlaceholders = [];
|
|
17215
|
+
let p = 1;
|
|
17216
|
+
for (const record of records) {
|
|
17217
|
+
const placeholders = columns.map((col) => {
|
|
17218
|
+
const raw = record[col];
|
|
17219
|
+
if (JSONB_COLUMNS.has(col)) {
|
|
17220
|
+
values.push(encodeJsonb(raw));
|
|
17221
|
+
return `$${p++}::jsonb`;
|
|
17222
|
+
}
|
|
17223
|
+
if (TEXT_ARRAY_COLUMNS.has(col)) {
|
|
17224
|
+
values.push(Array.isArray(raw) ? raw : []);
|
|
17225
|
+
return `$${p++}::text[]`;
|
|
17226
|
+
}
|
|
17227
|
+
values.push(raw === void 0 ? null : raw);
|
|
17228
|
+
return `$${p++}`;
|
|
17229
|
+
});
|
|
17230
|
+
rowPlaceholders.push(`(${placeholders.join(", ")})`);
|
|
17231
|
+
}
|
|
17232
|
+
return {
|
|
17233
|
+
text: `INSERT INTO ${qualifiedTable(schema, table)} (${quotedColumns})
|
|
17234
|
+
VALUES ${rowPlaceholders.join(", ")}
|
|
17235
|
+
${onConflict}`,
|
|
17236
|
+
values
|
|
17237
|
+
};
|
|
17238
|
+
}
|
|
17239
|
+
/** Primary key of the span table; also the conflict target for span upserts. */
|
|
17240
|
+
const SPAN_KEY_COLUMNS = [
|
|
17241
|
+
"traceId",
|
|
17242
|
+
"spanId",
|
|
17243
|
+
"endedAt"
|
|
17244
|
+
];
|
|
17245
|
+
/**
|
|
17246
|
+
* Conflict clause for span writes.
|
|
17247
|
+
*
|
|
17248
|
+
* A span is written twice under the event-sourced strategy: once when it starts
|
|
17249
|
+
* (`isPending = true`, `endedAt` synthesized from `startedAt`) and once when it
|
|
17250
|
+
* ends. Those two rows normally differ in `endedAt` and so coexist under the
|
|
17251
|
+
* primary key. A zero-duration span is the exception — its real `endedAt`
|
|
17252
|
+
* equals its `startedAt`, so the end row collides with the pending row. Letting
|
|
17253
|
+
* `DO NOTHING` win there would strand the span as permanently running, so the
|
|
17254
|
+
* end row overwrites the pending one instead.
|
|
17255
|
+
*
|
|
17256
|
+
* The `WHERE` guard keeps the clause a no-op for every other conflict, which
|
|
17257
|
+
* preserves insert-only retry idempotency: a replayed end row cannot clobber
|
|
17258
|
+
* the row it duplicates, and a late-arriving start row cannot revert an ended
|
|
17259
|
+
* span to pending.
|
|
17260
|
+
*/
|
|
17261
|
+
function spanConflictClause(columns) {
|
|
17262
|
+
return `ON CONFLICT ("traceId", "spanId", "endedAt") DO UPDATE SET ${columns.filter((column) => !SPAN_KEY_COLUMNS.includes(column)).map((column) => `"${column}" = EXCLUDED."${column}"`).join(", ")}
|
|
17263
|
+
WHERE ${TABLE_SPAN_EVENTS}."isPending" AND NOT EXCLUDED."isPending"`;
|
|
17264
|
+
}
|
|
17265
|
+
/**
|
|
17266
|
+
* Ordering that picks the winning row for a span under the event-sourced write
|
|
17267
|
+
* model. An ended row always beats a pending row for the same span; among rows
|
|
17268
|
+
* of the same kind the latest write wins, with `cursorId` breaking ties between
|
|
17269
|
+
* rows that share a timestamp. Combine with `DISTINCT ON` (or `LIMIT 1`) after
|
|
17270
|
+
* the grouping key to collapse a span's rows down to one.
|
|
17271
|
+
*/
|
|
17272
|
+
const SPAN_COLLAPSE_ORDER = "\"isPending\" ASC, \"endedAt\" DESC, \"cursorId\" DESC";
|
|
17273
|
+
/**
|
|
17274
|
+
* Standard SELECT column list for tracing tables. The select projects every
|
|
17275
|
+
* column the row→record converters expect.
|
|
17276
|
+
*/
|
|
17277
|
+
const SPAN_SELECT_COLUMNS = `
|
|
17278
|
+
${buildSelectColumns(SPAN_EVENT_COLUMNS)}`;
|
|
17279
|
+
const SPAN_LIGHT_SELECT_COLUMNS = `
|
|
17280
|
+
${buildNamedSelectColumns(SPAN_LIGHT_SELECT_COLUMN_NAMES)}`;
|
|
17281
|
+
const METRIC_SELECT_COLUMNS = `
|
|
17282
|
+
${buildSelectColumns(METRIC_EVENT_COLUMNS)}`;
|
|
17283
|
+
const LOG_SELECT_COLUMNS = `
|
|
17284
|
+
${buildSelectColumns(LOG_EVENT_COLUMNS)}`;
|
|
17285
|
+
const SCORE_SELECT_COLUMNS = `
|
|
17286
|
+
${buildSelectColumns(SCORE_EVENT_COLUMNS)}`;
|
|
17287
|
+
const FEEDBACK_SELECT_COLUMNS = `
|
|
17288
|
+
${buildSelectColumns(FEEDBACK_EVENT_COLUMNS)}`;
|
|
17289
|
+
//#endregion
|
|
17088
17290
|
//#region src/storage/domains/observability/v-next/helpers.ts
|
|
17089
17291
|
const PROMOTED_KEYS = /* @__PURE__ */ new Set([
|
|
17090
17292
|
"experimentId",
|
|
@@ -17234,10 +17436,26 @@ function commonContextToRow(record) {
|
|
|
17234
17436
|
serviceName: record.serviceName ?? null
|
|
17235
17437
|
};
|
|
17236
17438
|
}
|
|
17439
|
+
/**
|
|
17440
|
+
* Serialize a span payload for its jsonb column and build its stored preview from the same value.
|
|
17441
|
+
* Nonblank upstream previews win (trimmed and capped like generated ones). The preview goes into a `text` column, which
|
|
17442
|
+
* rejects NUL, so it gets the same repair `toPgJson` applies to the payload.
|
|
17443
|
+
*/
|
|
17444
|
+
function spanPayload(value, upstream) {
|
|
17445
|
+
const encoded = encodeJsonbValue(value);
|
|
17446
|
+
const preview = storedPreview(upstream, value, encoded?.json);
|
|
17447
|
+
return {
|
|
17448
|
+
encoded,
|
|
17449
|
+
preview: preview === null ? null : toPgText(preview)
|
|
17450
|
+
};
|
|
17451
|
+
}
|
|
17237
17452
|
function spanRecordToRow(span) {
|
|
17238
17453
|
const isPending = !span.isEvent && span.endedAt == null;
|
|
17239
17454
|
const endedAt = span.isEvent ? span.startedAt : span.endedAt ?? span.startedAt;
|
|
17240
17455
|
const metadata = span.metadata ?? null;
|
|
17456
|
+
const input = spanPayload(span.input, span.inputPreview);
|
|
17457
|
+
const output = spanPayload(span.output, span.outputPreview);
|
|
17458
|
+
const error = spanPayload(span.error, span.errorPreview);
|
|
17241
17459
|
return {
|
|
17242
17460
|
...commonContextToRow(span),
|
|
17243
17461
|
traceId: span.traceId,
|
|
@@ -17255,10 +17473,13 @@ function spanRecordToRow(span) {
|
|
|
17255
17473
|
scope: jsonField(span.scope),
|
|
17256
17474
|
attributes: jsonField(span.attributes),
|
|
17257
17475
|
links: jsonField(span.links),
|
|
17258
|
-
input:
|
|
17259
|
-
output:
|
|
17260
|
-
error:
|
|
17261
|
-
requestContext: jsonField(span.requestContext)
|
|
17476
|
+
input: input.encoded,
|
|
17477
|
+
output: output.encoded,
|
|
17478
|
+
error: error.encoded,
|
|
17479
|
+
requestContext: jsonField(span.requestContext),
|
|
17480
|
+
inputPreview: input.preview,
|
|
17481
|
+
outputPreview: output.preview,
|
|
17482
|
+
errorPreview: error.preview
|
|
17262
17483
|
};
|
|
17263
17484
|
}
|
|
17264
17485
|
/**
|
|
@@ -17325,6 +17546,32 @@ function rowToLightSpanRecord(row) {
|
|
|
17325
17546
|
updatedAt: null
|
|
17326
17547
|
};
|
|
17327
17548
|
}
|
|
17549
|
+
/** A stored preview as lists return it: `''` (nothing to preview) and NULL become null. */
|
|
17550
|
+
function listPreview(value) {
|
|
17551
|
+
return readPreview(value);
|
|
17552
|
+
}
|
|
17553
|
+
/**
|
|
17554
|
+
* Build a trace-list {@link LightSpanRecord} from a row that projected the light trace-list columns
|
|
17555
|
+
* (see `TRACE_LIGHT_SELECT_COLUMNS` in traces.ts): the timeline fields plus the memory context,
|
|
17556
|
+
* metadata and the stored previews. Callers fill in previews for rows written before they were
|
|
17557
|
+
* stored first.
|
|
17558
|
+
*/
|
|
17559
|
+
function rowToLightTraceRecord(row) {
|
|
17560
|
+
const record = rowToLightSpanRecord(row);
|
|
17561
|
+
return {
|
|
17562
|
+
...record,
|
|
17563
|
+
status: (0, _mastra_core_storage.computeTraceStatus)({
|
|
17564
|
+
error: record.error,
|
|
17565
|
+
endedAt: record.endedAt
|
|
17566
|
+
}),
|
|
17567
|
+
threadId: row.threadId == null ? null : String(row.threadId),
|
|
17568
|
+
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
17569
|
+
metadata: parsedJson(row.metadataRaw) ?? void 0,
|
|
17570
|
+
inputPreview: listPreview(row.inputPreview),
|
|
17571
|
+
outputPreview: listPreview(row.outputPreview),
|
|
17572
|
+
errorPreview: listPreview(row.errorPreview)
|
|
17573
|
+
};
|
|
17574
|
+
}
|
|
17328
17575
|
function logRecordToRow(log) {
|
|
17329
17576
|
return {
|
|
17330
17577
|
...commonContextToRow(log),
|
|
@@ -17840,119 +18087,6 @@ const COMPLEX_GROUP_BY_EXCLUDED = /* @__PURE__ */ new Set([
|
|
|
17840
18087
|
"requestContext"
|
|
17841
18088
|
]);
|
|
17842
18089
|
//#endregion
|
|
17843
|
-
//#region src/storage/domains/observability/v-next/sql.ts
|
|
17844
|
-
/**
|
|
17845
|
-
* SQL helpers for the v-next Postgres observability domain.
|
|
17846
|
-
*
|
|
17847
|
-
* Provides a multi-row INSERT builder with a configurable conflict clause
|
|
17848
|
-
* (`ON CONFLICT DO NOTHING` by default, for retry idempotency), and explicit
|
|
17849
|
-
* jsonb / text[] casts so the pg driver doesn't have to guess column types.
|
|
17850
|
-
*/
|
|
17851
|
-
/**
|
|
17852
|
-
* Encode a JS value for a `$N::jsonb` cast. Always `JSON.stringify` so a
|
|
17853
|
-
* plain string like `"hello"` becomes `"hello"` (a valid JSON scalar) and
|
|
17854
|
-
* not the bare word `hello`, which Postgres rejects when cast to jsonb.
|
|
17855
|
-
*
|
|
17856
|
-
* The result is sanitized because Postgres rejects some sequences that are
|
|
17857
|
-
* legal in JSON: NUL (`\u0000`) fails with 22P05 and unpaired UTF-16
|
|
17858
|
-
* surrogates fail with 22P02. Inserts here are batched into a single
|
|
17859
|
-
* multi-row statement, so one bad value would otherwise reject the whole
|
|
17860
|
-
* batch of observability events.
|
|
17861
|
-
*/
|
|
17862
|
-
function encodeJsonb(value) {
|
|
17863
|
-
if (value === null || value === void 0) return null;
|
|
17864
|
-
return toPgJson(value);
|
|
17865
|
-
}
|
|
17866
|
-
/**
|
|
17867
|
-
* Build a multi-row INSERT with explicit column types and ON CONFLICT DO NOTHING.
|
|
17868
|
-
*
|
|
17869
|
-
* @param schema Schema name.
|
|
17870
|
-
* @param table Table name.
|
|
17871
|
-
* @param records Array of records (each is a column-name → value object).
|
|
17872
|
-
* All records must have identical key sets.
|
|
17873
|
-
* @returns { text, values } ready to pass to `client.query`.
|
|
17874
|
-
*/
|
|
17875
|
-
function buildInsert(schema, table, records, onConflict = "ON CONFLICT DO NOTHING") {
|
|
17876
|
-
if (records.length === 0) return null;
|
|
17877
|
-
const columns = Object.keys(records[0]).map((c) => (0, _mastra_core_utils.parseSqlIdentifier)(c, "column name"));
|
|
17878
|
-
const quotedColumns = columns.map((c) => `"${c}"`).join(", ");
|
|
17879
|
-
const values = [];
|
|
17880
|
-
const rowPlaceholders = [];
|
|
17881
|
-
let p = 1;
|
|
17882
|
-
for (const record of records) {
|
|
17883
|
-
const placeholders = columns.map((col) => {
|
|
17884
|
-
const raw = record[col];
|
|
17885
|
-
if (JSONB_COLUMNS.has(col)) {
|
|
17886
|
-
values.push(encodeJsonb(raw));
|
|
17887
|
-
return `$${p++}::jsonb`;
|
|
17888
|
-
}
|
|
17889
|
-
if (TEXT_ARRAY_COLUMNS.has(col)) {
|
|
17890
|
-
values.push(Array.isArray(raw) ? raw : []);
|
|
17891
|
-
return `$${p++}::text[]`;
|
|
17892
|
-
}
|
|
17893
|
-
values.push(raw === void 0 ? null : raw);
|
|
17894
|
-
return `$${p++}`;
|
|
17895
|
-
});
|
|
17896
|
-
rowPlaceholders.push(`(${placeholders.join(", ")})`);
|
|
17897
|
-
}
|
|
17898
|
-
return {
|
|
17899
|
-
text: `INSERT INTO ${qualifiedTable(schema, table)} (${quotedColumns})
|
|
17900
|
-
VALUES ${rowPlaceholders.join(", ")}
|
|
17901
|
-
${onConflict}`,
|
|
17902
|
-
values
|
|
17903
|
-
};
|
|
17904
|
-
}
|
|
17905
|
-
/** Primary key of the span table; also the conflict target for span upserts. */
|
|
17906
|
-
const SPAN_KEY_COLUMNS = [
|
|
17907
|
-
"traceId",
|
|
17908
|
-
"spanId",
|
|
17909
|
-
"endedAt"
|
|
17910
|
-
];
|
|
17911
|
-
/**
|
|
17912
|
-
* Conflict clause for span writes.
|
|
17913
|
-
*
|
|
17914
|
-
* A span is written twice under the event-sourced strategy: once when it starts
|
|
17915
|
-
* (`isPending = true`, `endedAt` synthesized from `startedAt`) and once when it
|
|
17916
|
-
* ends. Those two rows normally differ in `endedAt` and so coexist under the
|
|
17917
|
-
* primary key. A zero-duration span is the exception — its real `endedAt`
|
|
17918
|
-
* equals its `startedAt`, so the end row collides with the pending row. Letting
|
|
17919
|
-
* `DO NOTHING` win there would strand the span as permanently running, so the
|
|
17920
|
-
* end row overwrites the pending one instead.
|
|
17921
|
-
*
|
|
17922
|
-
* The `WHERE` guard keeps the clause a no-op for every other conflict, which
|
|
17923
|
-
* preserves insert-only retry idempotency: a replayed end row cannot clobber
|
|
17924
|
-
* the row it duplicates, and a late-arriving start row cannot revert an ended
|
|
17925
|
-
* span to pending.
|
|
17926
|
-
*/
|
|
17927
|
-
function spanConflictClause(columns) {
|
|
17928
|
-
return `ON CONFLICT ("traceId", "spanId", "endedAt") DO UPDATE SET ${columns.filter((column) => !SPAN_KEY_COLUMNS.includes(column)).map((column) => `"${column}" = EXCLUDED."${column}"`).join(", ")}
|
|
17929
|
-
WHERE ${TABLE_SPAN_EVENTS}."isPending" AND NOT EXCLUDED."isPending"`;
|
|
17930
|
-
}
|
|
17931
|
-
/**
|
|
17932
|
-
* Ordering that picks the winning row for a span under the event-sourced write
|
|
17933
|
-
* model. An ended row always beats a pending row for the same span; among rows
|
|
17934
|
-
* of the same kind the latest write wins, with `cursorId` breaking ties between
|
|
17935
|
-
* rows that share a timestamp. Combine with `DISTINCT ON` (or `LIMIT 1`) after
|
|
17936
|
-
* the grouping key to collapse a span's rows down to one.
|
|
17937
|
-
*/
|
|
17938
|
-
const SPAN_COLLAPSE_ORDER = "\"isPending\" ASC, \"endedAt\" DESC, \"cursorId\" DESC";
|
|
17939
|
-
/**
|
|
17940
|
-
* Standard SELECT column list for tracing tables. The select projects every
|
|
17941
|
-
* column the row→record converters expect.
|
|
17942
|
-
*/
|
|
17943
|
-
const SPAN_SELECT_COLUMNS = `
|
|
17944
|
-
${buildSelectColumns(SPAN_EVENT_COLUMNS)}`;
|
|
17945
|
-
const SPAN_LIGHT_SELECT_COLUMNS = `
|
|
17946
|
-
${buildNamedSelectColumns(SPAN_LIGHT_SELECT_COLUMN_NAMES)}`;
|
|
17947
|
-
const METRIC_SELECT_COLUMNS = `
|
|
17948
|
-
${buildSelectColumns(METRIC_EVENT_COLUMNS)}`;
|
|
17949
|
-
const LOG_SELECT_COLUMNS = `
|
|
17950
|
-
${buildSelectColumns(LOG_EVENT_COLUMNS)}`;
|
|
17951
|
-
const SCORE_SELECT_COLUMNS = `
|
|
17952
|
-
${buildSelectColumns(SCORE_EVENT_COLUMNS)}`;
|
|
17953
|
-
const FEEDBACK_SELECT_COLUMNS = `
|
|
17954
|
-
${buildSelectColumns(FEEDBACK_EVENT_COLUMNS)}`;
|
|
17955
|
-
//#endregion
|
|
17956
18090
|
//#region src/storage/domains/observability/v-next/feedback.ts
|
|
17957
18091
|
/**
|
|
17958
18092
|
* Feedback operations for the v-next Postgres observability domain.
|
|
@@ -19111,7 +19245,10 @@ const TRACE_SELECT = `
|
|
|
19111
19245
|
r."entityId" AS "entityId",
|
|
19112
19246
|
r."parentSpanId" AS "parentSpanId",
|
|
19113
19247
|
r."metadataRaw" AS "metadata",
|
|
19114
|
-
r."
|
|
19248
|
+
r."inputPreview" AS "inputPreview",
|
|
19249
|
+
r."outputPreview" AS "outputPreview",
|
|
19250
|
+
r."errorPreview" AS "errorPreview",
|
|
19251
|
+
r."cursorId" AS "cursorId",
|
|
19115
19252
|
r."threadId" AS "threadId",
|
|
19116
19253
|
r."resourceId" AS "resourceId",
|
|
19117
19254
|
r."startedAt" AS "startedAt",
|
|
@@ -19569,7 +19706,7 @@ function compilePostgresTraceQuery(schema, plan, mode = "data", safeHorizon) {
|
|
|
19569
19706
|
safeHorizon
|
|
19570
19707
|
};
|
|
19571
19708
|
}
|
|
19572
|
-
const { ctes, values } = compilePostgresTraceCandidates(schema, plan, `${TRACE_SELECT}${plan.paginationMode === "delta" ? ", r.\"xactId\"
|
|
19709
|
+
const { ctes, values } = compilePostgresTraceCandidates(schema, plan, `${TRACE_SELECT}${plan.paginationMode === "delta" ? ", r.\"xactId\"" : ""}`, deltaWindow);
|
|
19573
19710
|
const candidates = `WITH ${ctes.join(",\n")}`;
|
|
19574
19711
|
if (plan.paginationMode === "page" && mode === "count") return {
|
|
19575
19712
|
text: `${candidates}
|
|
@@ -19811,6 +19948,34 @@ async function setRemainingTimeout(transaction, deadline) {
|
|
|
19811
19948
|
if (remainingTimeoutMs <= 0) throw new _mastra_core_storage.TraceQueryExecutionError();
|
|
19812
19949
|
await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${remainingTimeoutMs}ms`]);
|
|
19813
19950
|
}
|
|
19951
|
+
const PREVIEW_PAYLOADS$2 = [
|
|
19952
|
+
["inputPreview", "input"],
|
|
19953
|
+
["outputPreview", "output"],
|
|
19954
|
+
["errorPreview", "error"]
|
|
19955
|
+
];
|
|
19956
|
+
/**
|
|
19957
|
+
* Fill the previews of root rows written before the preview columns existed (stored preview NULL)
|
|
19958
|
+
* by reading just those rows' payloads, and only the payloads whose preview is missing. Rows are
|
|
19959
|
+
* keyed by `cursorId`, which identifies the exact root row the list picked.
|
|
19960
|
+
*
|
|
19961
|
+
* jsonb comes back decoded, so the preview is built with `buildPreview(value)`. jsonb also
|
|
19962
|
+
* normalizes object key order, so a compact-JSON fallback preview can differ from the one the
|
|
19963
|
+
* writer would have stored from the original value. That is acceptable for these old rows.
|
|
19964
|
+
*/
|
|
19965
|
+
async function fillMissingTracePreviews(transaction, schema, rows, deadline) {
|
|
19966
|
+
const missing = rows.filter((row) => PREVIEW_PAYLOADS$2.some(([preview]) => needsPreview(row[preview])));
|
|
19967
|
+
if (!missing.length) return;
|
|
19968
|
+
await setRemainingTimeout(transaction, deadline);
|
|
19969
|
+
const payloads = await transaction.any(`SELECT e."cursorId"::text AS "cursorId",
|
|
19970
|
+
${PREVIEW_PAYLOADS$2.map(([preview, payload]) => `${missing.some((row) => needsPreview(row[preview])) ? `e."${payload}"` : "NULL"} AS "${payload}"`).join(",\n ")}
|
|
19971
|
+
FROM ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} e
|
|
19972
|
+
WHERE (e."traceId", e."cursorId") IN (SELECT * FROM unnest($1::text[], $2::bigint[]))`, [missing.map((row) => String(row.traceId)), missing.map((row) => String(row.cursorId))]);
|
|
19973
|
+
const byCursor = new Map(payloads.map((payload) => [String(payload.cursorId), payload]));
|
|
19974
|
+
for (const row of missing) {
|
|
19975
|
+
const payload = byCursor.get(String(row.cursorId));
|
|
19976
|
+
for (const [preview, column] of PREVIEW_PAYLOADS$2) if (needsPreview(row[preview])) row[preview] = readPreview(row[preview], payload?.[column]);
|
|
19977
|
+
}
|
|
19978
|
+
}
|
|
19814
19979
|
function traceRowToResult(row) {
|
|
19815
19980
|
return {
|
|
19816
19981
|
traceId: String(row.traceId),
|
|
@@ -19820,7 +19985,9 @@ function traceRowToResult(row) {
|
|
|
19820
19985
|
parentSpanId: row.parentSpanId ?? null,
|
|
19821
19986
|
createdAt: asIsoTimestamp$1(row.startedAt),
|
|
19822
19987
|
metadata: row.metadata ?? null,
|
|
19823
|
-
inputPreview:
|
|
19988
|
+
inputPreview: readPreview(row.inputPreview),
|
|
19989
|
+
outputPreview: readPreview(row.outputPreview),
|
|
19990
|
+
errorPreview: readPreview(row.errorPreview),
|
|
19824
19991
|
threadId: row.threadId == null ? null : String(row.threadId),
|
|
19825
19992
|
resourceId: row.resourceId == null ? null : String(row.resourceId),
|
|
19826
19993
|
startedAt: asIsoTimestamp$1(row.startedAt),
|
|
@@ -19848,6 +20015,7 @@ async function queryTraces(client, schema, plan, timeoutMs) {
|
|
|
19848
20015
|
rows = await transaction.any(query.text, query.values);
|
|
19849
20016
|
}
|
|
19850
20017
|
const visible = rows.slice(0, plan.limit);
|
|
20018
|
+
await fillMissingTracePreviews(transaction, schema, visible, deadline);
|
|
19851
20019
|
const last = visible.at(-1);
|
|
19852
20020
|
return _mastra_core_storage.traceQueryResponseSchema.parse({
|
|
19853
20021
|
traces: visible.map(traceRowToResult),
|
|
@@ -19877,6 +20045,7 @@ async function queryTraces(client, schema, plan, timeoutMs) {
|
|
|
19877
20045
|
if (remainingTimeoutMs <= 0) throw new _mastra_core_storage.TraceQueryExecutionError();
|
|
19878
20046
|
await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${remainingTimeoutMs}ms`]);
|
|
19879
20047
|
const rows = await transaction.any(dataQuery.text, dataQuery.values);
|
|
20048
|
+
await fillMissingTracePreviews(transaction, schema, rows, deadline);
|
|
19880
20049
|
return {
|
|
19881
20050
|
total: Number(countRows[0]?.count ?? 0),
|
|
19882
20051
|
rows,
|
|
@@ -19896,7 +20065,12 @@ async function queryTraces(client, schema, plan, timeoutMs) {
|
|
|
19896
20065
|
});
|
|
19897
20066
|
}
|
|
19898
20067
|
const query = compilePostgresTraceQuery(schema, plan);
|
|
19899
|
-
const
|
|
20068
|
+
const deadline = performance.now() + _mastra_core_storage.resolveTraceQueryTimeoutMs(timeoutMs);
|
|
20069
|
+
const rows = await runWithPostgresTraceQueryTimeout(client, timeoutMs, async (transaction) => {
|
|
20070
|
+
const rows = await transaction.any(query.text, query.values);
|
|
20071
|
+
if (plan.result === "traces") await fillMissingTracePreviews(transaction, schema, rows.slice(0, plan.limit), deadline);
|
|
20072
|
+
return rows;
|
|
20073
|
+
});
|
|
19900
20074
|
const visibleRows = rows.slice(0, plan.limit);
|
|
19901
20075
|
if (plan.result === "groups") {
|
|
19902
20076
|
const groups = visibleRows.map((row) => ({ threadId: String(row.threadId) }));
|
|
@@ -20053,6 +20227,29 @@ function compilePostgresSpanQuery(schema, plan) {
|
|
|
20053
20227
|
values
|
|
20054
20228
|
};
|
|
20055
20229
|
}
|
|
20230
|
+
const PREVIEW_PAYLOADS$1 = [
|
|
20231
|
+
["inputPreview", "input"],
|
|
20232
|
+
["outputPreview", "output"],
|
|
20233
|
+
["errorPreview", "error"]
|
|
20234
|
+
];
|
|
20235
|
+
/**
|
|
20236
|
+
* Stored previews of a hydrated row, built from the payload where the stored preview is NULL.
|
|
20237
|
+
* jsonb comes back decoded, so the fallback uses `buildPreview(value)`. jsonb normalizes object key
|
|
20238
|
+
* order, so a compact-JSON fallback preview can differ from what the writer would have stored from
|
|
20239
|
+
* the original value; that is acceptable for rows written before the preview columns existed.
|
|
20240
|
+
*/
|
|
20241
|
+
function toSpanQueryPayload(row) {
|
|
20242
|
+
const [inputPreview, outputPreview, errorPreview] = PREVIEW_PAYLOADS$1.map(([preview, payload]) => readPreview(row[preview], row[payload]));
|
|
20243
|
+
return {
|
|
20244
|
+
organizationId: row.organizationId ?? null,
|
|
20245
|
+
resourceId: row.resourceId ?? null,
|
|
20246
|
+
traceId: String(row.traceId),
|
|
20247
|
+
spanId: String(row.spanId),
|
|
20248
|
+
inputPreview,
|
|
20249
|
+
outputPreview,
|
|
20250
|
+
errorPreview
|
|
20251
|
+
};
|
|
20252
|
+
}
|
|
20056
20253
|
async function querySpans(client, schema, plan, timeoutMs) {
|
|
20057
20254
|
const deadline = performance.now() + _mastra_core_storage.resolveTraceQueryTimeoutMs(timeoutMs);
|
|
20058
20255
|
return runWithPostgresTraceQueryTimeout(client, timeoutMs, async (tx) => {
|
|
@@ -20077,9 +20274,9 @@ async function querySpans(client, schema, plan, timeoutMs) {
|
|
|
20077
20274
|
payloadValues.push(row.version);
|
|
20078
20275
|
return `(${key} AND "cursorId" = $${payloadValues.length}::bigint)`;
|
|
20079
20276
|
}).join(" OR ");
|
|
20080
|
-
const payloads = await tx.any(`SELECT ${identities},
|
|
20081
|
-
|
|
20082
|
-
FROM ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} WHERE ${payloadWhere}`, payloadValues);
|
|
20277
|
+
const payloads = (await tx.any(`SELECT ${identities},
|
|
20278
|
+
${PREVIEW_PAYLOADS$1.map(([preview, payload]) => `"${preview}", CASE WHEN ${missingPreviewSql(`"${preview}"`)} THEN "${payload}" END AS "${payload}"`).join(",\n ")}
|
|
20279
|
+
FROM ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} WHERE ${payloadWhere}`, payloadValues)).map(toSpanQueryPayload);
|
|
20083
20280
|
await remaining();
|
|
20084
20281
|
const metricValues = [];
|
|
20085
20282
|
const metricWhere = selectedKeys(page, metricValues);
|
|
@@ -20090,7 +20287,7 @@ async function querySpans(client, schema, plan, timeoutMs) {
|
|
|
20090
20287
|
ORDER BY ${identities}, "metricId", "cursorId" DESC) m
|
|
20091
20288
|
LIMIT ${_mastra_core_storage.SPAN_QUERY_MAX_COST_METRICS + 1}`, metricValues);
|
|
20092
20289
|
if (metrics.length > _mastra_core_storage.SPAN_QUERY_MAX_COST_METRICS) throw new _mastra_core_storage.TraceQueryResourceLimitError();
|
|
20093
|
-
return
|
|
20290
|
+
return buildSpanQueryResponse(plan, selected, payloads, metrics);
|
|
20094
20291
|
}, { repeatableRead: true });
|
|
20095
20292
|
}
|
|
20096
20293
|
//#endregion
|
|
@@ -20555,16 +20752,73 @@ function buildListTracesFilters(filters, spanTable, nextParamIdx) {
|
|
|
20555
20752
|
* array-of-strings representation here.
|
|
20556
20753
|
*/
|
|
20557
20754
|
const SPAN_SELECT_COLUMNS_ALIASED = SPAN_SELECT_COLUMNS.replace(/\n/g, " ").split(",").map((c) => `r.${c.trim()}`).join(", ");
|
|
20755
|
+
const FULL_PROJECTION = {
|
|
20756
|
+
columns: SPAN_SELECT_COLUMNS_ALIASED,
|
|
20757
|
+
mapRows: (_client, _span, rows) => (0, _mastra_core_storage.toTraceSpans)(rows.map(rowToSpanRecord))
|
|
20758
|
+
};
|
|
20759
|
+
/**
|
|
20760
|
+
* Columns a light trace list renders: the timeline fields, memory context, metadata and the stored
|
|
20761
|
+
* previews. The `input` / `output` payloads stay off the read path; rows written before the
|
|
20762
|
+
* previews were stored get theirs from the payloads, read for just those rows.
|
|
20763
|
+
*/
|
|
20764
|
+
const TRACE_LIGHT_SELECT_COLUMNS = [
|
|
20765
|
+
...SPAN_LIGHT_SELECT_COLUMN_NAMES,
|
|
20766
|
+
"threadId",
|
|
20767
|
+
"resourceId",
|
|
20768
|
+
"metadataRaw",
|
|
20769
|
+
"inputPreview",
|
|
20770
|
+
"outputPreview",
|
|
20771
|
+
"errorPreview"
|
|
20772
|
+
].map((column) => `r."${column}"`).join(", ");
|
|
20773
|
+
const PREVIEW_PAYLOADS = [
|
|
20774
|
+
["inputPreview", "input"],
|
|
20775
|
+
["outputPreview", "output"],
|
|
20776
|
+
["errorPreview", "error"]
|
|
20777
|
+
];
|
|
20778
|
+
/**
|
|
20779
|
+
* Builds the previews of rows written before previews were stored (NULL preview) from those rows'
|
|
20780
|
+
* payloads. Only the flagged rows' payloads are read, so this fades out as old rows age out.
|
|
20781
|
+
*
|
|
20782
|
+
* `jsonb` comes back decoded with its keys reordered, so a compact-JSON preview built here can
|
|
20783
|
+
* differ in key order from the one the writer would have stored. Acceptable for old rows.
|
|
20784
|
+
*/
|
|
20785
|
+
async function fillMissingPreviews(client, span, rows) {
|
|
20786
|
+
const missing = rows.filter((row) => PREVIEW_PAYLOADS.some(([preview]) => needsPreview(row[preview])));
|
|
20787
|
+
if (!missing.length) return;
|
|
20788
|
+
const payloads = await client.manyOrNone(`SELECT e."cursorId"::text AS "cursorId",
|
|
20789
|
+
${PREVIEW_PAYLOADS.map(([preview, payload]) => `${missing.some((row) => needsPreview(row[preview])) ? `e."${payload}"` : "NULL"} AS "${payload}"`).join(",\n ")}
|
|
20790
|
+
FROM ${span} e
|
|
20791
|
+
WHERE (e."traceId", e."cursorId") IN (SELECT * FROM unnest($1::text[], $2::bigint[]))`, [missing.map((row) => String(row.traceId)), missing.map((row) => String(row.cursorId))]);
|
|
20792
|
+
const byCursor = new Map(payloads.map((payload) => [String(payload.cursorId), payload]));
|
|
20793
|
+
for (const row of missing) {
|
|
20794
|
+
const payload = byCursor.get(String(row.cursorId));
|
|
20795
|
+
for (const [preview, column] of PREVIEW_PAYLOADS) if (needsPreview(row[preview])) row[preview] = readPreview(row[preview], payload?.[column]);
|
|
20796
|
+
}
|
|
20797
|
+
}
|
|
20798
|
+
const LIGHT_PROJECTION = {
|
|
20799
|
+
columns: TRACE_LIGHT_SELECT_COLUMNS,
|
|
20800
|
+
mapRows: async (client, span, rows) => {
|
|
20801
|
+
await fillMissingPreviews(client, span, rows);
|
|
20802
|
+
return rows.map(rowToLightTraceRecord);
|
|
20803
|
+
}
|
|
20804
|
+
};
|
|
20558
20805
|
async function listTraces(client, schema, args) {
|
|
20806
|
+
return listTraceRows(client, schema, args, FULL_PROJECTION);
|
|
20807
|
+
}
|
|
20808
|
+
/** Trace list projecting only the columns a list renders, with stored previews instead of payloads. */
|
|
20809
|
+
async function listTracesLight(client, schema, args) {
|
|
20810
|
+
return listTraceRows(client, schema, args, LIGHT_PROJECTION);
|
|
20811
|
+
}
|
|
20812
|
+
async function listTraceRows(client, schema, args, projection) {
|
|
20559
20813
|
const { mode, filters, pagination, orderBy, after, limit } = _mastra_core_storage.listTracesArgsSchema.parse(args);
|
|
20560
20814
|
const span = qualifiedTable(schema, TABLE_SPAN_EVENTS);
|
|
20561
20815
|
if (mode === "delta") {
|
|
20562
20816
|
assertDeltaPollingEnabled();
|
|
20563
|
-
return listTracesDelta(client, span, filters, after, limit);
|
|
20817
|
+
return listTracesDelta(client, span, projection, filters, after, limit);
|
|
20564
20818
|
}
|
|
20565
|
-
return listTracesPage(client, span, filters, pagination.page, pagination.perPage, orderBy.field, orderBy.direction);
|
|
20819
|
+
return listTracesPage(client, span, projection, filters, pagination.page, pagination.perPage, orderBy.field, orderBy.direction);
|
|
20566
20820
|
}
|
|
20567
|
-
async function listTracesPage(client, span, filters, page, perPage, orderField, orderDir) {
|
|
20821
|
+
async function listTracesPage(client, span, projection, filters, page, perPage, orderField, orderDir) {
|
|
20568
20822
|
const { conditions, params, nextParamIdx } = buildListTracesFilters(filters, span, 1);
|
|
20569
20823
|
let i = nextParamIdx;
|
|
20570
20824
|
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
@@ -20572,15 +20826,18 @@ async function listTracesPage(client, span, filters, page, perPage, orderField,
|
|
|
20572
20826
|
const countRow = await client.oneOrNone(`SELECT COUNT(*)::text AS count FROM ${span} r ${whereClause}`, params);
|
|
20573
20827
|
const count = Number(countRow?.count ?? 0);
|
|
20574
20828
|
let spans = [];
|
|
20575
|
-
if (count > 0)
|
|
20829
|
+
if (count > 0) {
|
|
20830
|
+
const rows = await client.manyOrNone(`SELECT ${projection.columns}
|
|
20576
20831
|
FROM ${span} r
|
|
20577
20832
|
${whereClause}
|
|
20578
20833
|
${orderClause}
|
|
20579
20834
|
LIMIT $${i++} OFFSET $${i++}`, [
|
|
20580
|
-
|
|
20581
|
-
|
|
20582
|
-
|
|
20583
|
-
|
|
20835
|
+
...params,
|
|
20836
|
+
perPage,
|
|
20837
|
+
page * perPage
|
|
20838
|
+
]);
|
|
20839
|
+
spans = await projection.mapRows(client, span, rows);
|
|
20840
|
+
}
|
|
20584
20841
|
const deltaCursor = deltaPollingFeatureEnabled() ? await readTracesStreamHeadCursor(client, span, filters) : void 0;
|
|
20585
20842
|
return {
|
|
20586
20843
|
pagination: {
|
|
@@ -20589,11 +20846,11 @@ async function listTracesPage(client, span, filters, page, perPage, orderField,
|
|
|
20589
20846
|
perPage,
|
|
20590
20847
|
hasMore: (page + 1) * perPage < count
|
|
20591
20848
|
},
|
|
20592
|
-
spans
|
|
20849
|
+
spans,
|
|
20593
20850
|
...deltaCursor !== void 0 ? { deltaCursor } : {}
|
|
20594
20851
|
};
|
|
20595
20852
|
}
|
|
20596
|
-
async function listTracesDelta(client, span, filters, after, limit) {
|
|
20853
|
+
async function listTracesDelta(client, span, projection, filters, after, limit) {
|
|
20597
20854
|
if (after === void 0) {
|
|
20598
20855
|
const deltaCursor = await readTracesStreamHeadCursor(client, span, filters);
|
|
20599
20856
|
return {
|
|
@@ -20613,7 +20870,7 @@ async function listTracesDelta(client, span, filters, after, limit) {
|
|
|
20613
20870
|
params.push(afterCursor.xactId, afterCursor.cursorId);
|
|
20614
20871
|
conditions.push(`r."xactId" < $${i++}::xid8`);
|
|
20615
20872
|
params.push(safeHorizon);
|
|
20616
|
-
const rows = await client.manyOrNone(`SELECT ${
|
|
20873
|
+
const rows = await client.manyOrNone(`SELECT ${projection.columns}
|
|
20617
20874
|
FROM ${span} r
|
|
20618
20875
|
WHERE ${conditions.join(" AND ")}
|
|
20619
20876
|
ORDER BY r."xactId" ASC, r."cursorId" ASC
|
|
@@ -20622,7 +20879,7 @@ async function listTracesDelta(client, span, filters, after, limit) {
|
|
|
20622
20879
|
const visible = rows.slice(0, limit);
|
|
20623
20880
|
const deltaCursor = visible.length > 0 ? encodeDeltaCursor(visible[visible.length - 1].xactId, visible[visible.length - 1].cursorId) : encodeDeltaCursor(safeHorizon, 0);
|
|
20624
20881
|
return {
|
|
20625
|
-
spans: (
|
|
20882
|
+
spans: await projection.mapRows(client, span, visible),
|
|
20626
20883
|
delta: {
|
|
20627
20884
|
limit,
|
|
20628
20885
|
hasMore
|
|
@@ -21333,6 +21590,9 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
|
|
|
21333
21590
|
async listTraces(args) {
|
|
21334
21591
|
return this.#run("LIST_TRACES", () => listTraces(this.#readClient, this.#schema, args));
|
|
21335
21592
|
}
|
|
21593
|
+
async listTracesLight(args) {
|
|
21594
|
+
return this.#run("LIST_TRACES_LIGHT", () => listTracesLight(this.#readClient, this.#schema, args));
|
|
21595
|
+
}
|
|
21336
21596
|
async querySpans(plan) {
|
|
21337
21597
|
return this.#run("QUERY_SPANS", () => querySpans(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
|
|
21338
21598
|
}
|