@mastra/pg 1.32.0-alpha.3 → 1.32.0-alpha.4

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
@@ -19187,7 +19187,26 @@ async function getScorePercentiles(client, schema, args) {
19187
19187
  const TRACE_STATUS_SQL = `CASE WHEN r."error" IS NOT NULL THEN 'error' ELSE 'success' END`;
19188
19188
  /** Core versions without the field reject it in strict query results, so it is only returned when core knows it. */
19189
19189
  const timeToFirstChunkSupported = "timeToFirstChunkMs" in coreStorage.TRACE_QUERY_FIELD_REGISTRY.trace;
19190
- const timeToFirstChunkFeatures = timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [];
19190
+ /** Core before the sort rejects `orderBy` on the field, so the sort is only declared when core plans it. */
19191
+ const timeToFirstChunkSortSupported = coreStorage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstChunkMs") === true;
19192
+ const timeToFirstChunkFeatures = [...timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [], ...timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk-sort"] : []];
19193
+ /**
19194
+ * Keyset bounds for a cursor's sort value: terms for rows strictly past it in `direction`, and the
19195
+ * condition for rows level with it. Empty values sort last in both directions, so they are past
19196
+ * every value and level only with each other.
19197
+ */
19198
+ function keysetSortBounds(field, direction, sortValue, values) {
19199
+ if (sortValue === null) return {
19200
+ past: [],
19201
+ level: `${field} IS NULL`
19202
+ };
19203
+ values.push(sortValue);
19204
+ const parameter = `$${values.length}::${typeof sortValue === "number" ? "float8" : "timestamptz"}`;
19205
+ return {
19206
+ past: [`${field} ${direction === "asc" ? ">" : "<"} ${parameter}`, `${field} IS NULL`],
19207
+ level: `${field} = ${parameter}`
19208
+ };
19209
+ }
19191
19210
  function durationMsSql(startedAt, endedAt) {
19192
19211
  return `EXTRACT(EPOCH FROM (${endedAt} - ${startedAt}))::numeric * 1000`;
19193
19212
  }
@@ -19779,8 +19798,8 @@ LIMIT $${values.length}`,
19779
19798
  values
19780
19799
  };
19781
19800
  }
19782
- const orderField = plan.orderBy.field === "startedAt" ? "\"startedAt\"" : "\"endedAt\"";
19783
- const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
19801
+ const orderField = `"${plan.orderBy.field}"`;
19802
+ const direction = `${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST`;
19784
19803
  if (plan.paginationMode === "page") {
19785
19804
  values.push(plan.perPage, plan.page * plan.perPage);
19786
19805
  return {
@@ -19794,10 +19813,9 @@ LIMIT $${values.length - 1} OFFSET $${values.length}`,
19794
19813
  }
19795
19814
  let pageCondition = "";
19796
19815
  if (plan.cursor) {
19797
- const comparison = plan.orderBy.direction === "asc" ? ">" : "<";
19798
- const sortParameter = `$${values.length + 1}`;
19799
- pageCondition = `WHERE (${orderField} ${comparison} ${sortParameter} OR (${orderField} = ${sortParameter} AND "traceId" > ${`$${values.length + 2}`}))`;
19800
- values.push(plan.cursor.sortValue, plan.cursor.traceId);
19816
+ const { past, level } = keysetSortBounds(orderField, plan.orderBy.direction, plan.cursor.sortValue, values);
19817
+ values.push(plan.cursor.traceId);
19818
+ pageCondition = `WHERE (${[...past, `(${level} AND "traceId" > $${values.length})`].join(" OR ")})`;
19801
19819
  }
19802
19820
  values.push(plan.limit + 1);
19803
19821
  return {
@@ -20130,7 +20148,7 @@ async function queryTraces(client, schema, plan, timeoutMs) {
20130
20148
  traces,
20131
20149
  page: { next: rows.length > plan.limit && last ? coreStorage.encodeTraceQueryCursor(plan, {
20132
20150
  result: "traces",
20133
- sortValue: last[plan.orderBy.field],
20151
+ sortValue: last[plan.orderBy.field] ?? null,
20134
20152
  traceId: last.traceId
20135
20153
  }) : null }
20136
20154
  });
@@ -20202,11 +20220,8 @@ function selectedKeys(rows, values) {
20202
20220
  function afterCursor(plan, values) {
20203
20221
  if (!plan.cursor) return "TRUE";
20204
20222
  const cursor = plan.cursor;
20205
- values.push(cursor.sortValue);
20206
- const timestamp = `$${values.length}::timestamptz`;
20207
- const field = `s."${plan.orderBy.field}"`;
20208
- const terms = [`${field} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${timestamp}`];
20209
- const equal = [`${field} = ${timestamp}`];
20223
+ const { past: terms, level } = keysetSortBounds(`s."${plan.orderBy.field}"`, plan.orderBy.direction, cursor.sortValue, values);
20224
+ const equal = [level];
20210
20225
  for (const key of identityFields) {
20211
20226
  const ref = `s."${key}" COLLATE "C"`;
20212
20227
  const value = cursor[key];
@@ -20272,7 +20287,7 @@ function compilePostgresSpanQuery(schema, plan) {
20272
20287
  "version"
20273
20288
  ].map((field) => `s."${field}"`).join(", ")}
20274
20289
  FROM candidates s WHERE s."startedAt" >= $1 AND s."startedAt" < $2 AND (${predicate.sql}) AND ${after}
20275
- ORDER BY s."${plan.orderBy.field}" ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, ${identityOrder}
20290
+ ORDER BY s."${plan.orderBy.field}" ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, ${identityOrder}
20276
20291
  LIMIT $${values.length}`,
20277
20292
  values
20278
20293
  };