@mastra/pg 1.20.0 → 1.20.1-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.cjs CHANGED
@@ -2176,7 +2176,7 @@ async function loadSchemaSnapshot(client, schemaName) {
2176
2176
  const schema = schemaName || "public";
2177
2177
  const [tableRows, columnRows, indexRows] = await Promise.all([
2178
2178
  client.manyOrNone(`SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = $1`, [schema]),
2179
- client.manyOrNone(`SELECT c.relname AS table_name, a.attname AS column_name
2179
+ client.manyOrNone(`SELECT c.relname AS table_name, a.attname AS column_name, format_type(a.atttypid, a.atttypmod) AS data_type
2180
2180
  FROM pg_catalog.pg_class c
2181
2181
  JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
2182
2182
  JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
@@ -2191,6 +2191,7 @@ async function loadSchemaSnapshot(client, schemaName) {
2191
2191
  WHERE n.nspname = $1`, [schema])
2192
2192
  ]);
2193
2193
  const columns = /* @__PURE__ */ new Map();
2194
+ const columnTypes = /* @__PURE__ */ new Map();
2194
2195
  for (const row of columnRows) {
2195
2196
  let set = columns.get(row.table_name);
2196
2197
  if (!set) {
@@ -2198,6 +2199,12 @@ async function loadSchemaSnapshot(client, schemaName) {
2198
2199
  columns.set(row.table_name, set);
2199
2200
  }
2200
2201
  set.add(row.column_name);
2202
+ let types = columnTypes.get(row.table_name);
2203
+ if (!types) {
2204
+ types = /* @__PURE__ */ new Map();
2205
+ columnTypes.set(row.table_name, types);
2206
+ }
2207
+ types.set(row.column_name, row.data_type);
2201
2208
  }
2202
2209
  const indexes = /* @__PURE__ */ new Set();
2203
2210
  const replicaIdentityIndexes = /* @__PURE__ */ new Set();
@@ -2211,6 +2218,7 @@ async function loadSchemaSnapshot(client, schemaName) {
2211
2218
  schemaName: schema,
2212
2219
  tables: new Set(tableRows.map((r) => r.tablename)),
2213
2220
  columns,
2221
+ columnTypes,
2214
2222
  indexes,
2215
2223
  replicaIdentityIndexes,
2216
2224
  primaryKeyIndexes
@@ -2464,6 +2472,8 @@ var PgDB = class extends _mastra_core_base.MastraBase {
2464
2472
  skipDefaultIndexes;
2465
2473
  /** Cache of actual table columns: tableName -> Set<columnName> */
2466
2474
  tableColumnsCache = /* @__PURE__ */ new Map();
2475
+ /** Cache of column Postgres data types: tableName -> columnName -> data_type */
2476
+ columnTypeCache = /* @__PURE__ */ new Map();
2467
2477
  constructor(config) {
2468
2478
  super({
2469
2479
  component: "STORAGE",
@@ -2538,7 +2548,9 @@ var PgDB = class extends _mastra_core_base.MastraBase {
2538
2548
  }
2539
2549
  }
2540
2550
  this.tableColumnsCache.delete(oldName);
2551
+ this.columnTypeCache.delete(oldName);
2541
2552
  this.tableColumnsCache.delete(newName);
2553
+ this.columnTypeCache.delete(newName);
2542
2554
  }
2543
2555
  /**
2544
2556
  * Records an out-of-band `DROP TABLE` in the init snapshot. See
@@ -2554,8 +2566,10 @@ var PgDB = class extends _mastra_core_base.MastraBase {
2554
2566
  if (snapshot) {
2555
2567
  snapshot.tables.delete(tableName);
2556
2568
  snapshot.columns.delete(tableName);
2569
+ snapshot.columnTypes.delete(tableName);
2557
2570
  }
2558
2571
  this.tableColumnsCache.delete(tableName);
2572
+ this.columnTypeCache.delete(tableName);
2559
2573
  }
2560
2574
  /**
2561
2575
  * Records an out-of-band `ALTER TABLE … ADD COLUMN` in the init snapshot.
@@ -2565,6 +2579,7 @@ var PgDB = class extends _mastra_core_base.MastraBase {
2565
2579
  const snapshot = this.schemaSnapshot;
2566
2580
  if (snapshot) this.snapshotColumns(snapshot, tableName).add(column);
2567
2581
  this.tableColumnsCache.delete(tableName);
2582
+ this.columnTypeCache.delete(tableName);
2568
2583
  }
2569
2584
  /**
2570
2585
  * Gets the set of column names that actually exist in the database table.
@@ -2607,6 +2622,50 @@ var PgDB = class extends _mastra_core_base.MastraBase {
2607
2622
  ]);
2608
2623
  }
2609
2624
  /**
2625
+ * Returns the Postgres data type of a column (e.g. `jsonb`, `json`, `text`),
2626
+ * or null when the table or column does not exist.
2627
+ *
2628
+ * Answered from the init snapshot when one is installed, so a warm `init()`
2629
+ * issues no catalog probe. Outside init, results are cached per instance and
2630
+ * the cache is invalidated alongside {@link tableColumnsCache} whenever DDL
2631
+ * changes a table.
2632
+ */
2633
+ async getColumnType(table, column) {
2634
+ const snapshot = this.schemaSnapshot;
2635
+ if (snapshot) {
2636
+ const types = snapshot.columnTypes.get(table);
2637
+ const known = types?.get(column) ?? types?.get(column.toLowerCase());
2638
+ if (known) return known;
2639
+ if (types) return null;
2640
+ }
2641
+ const cached = this.columnTypeCache.get(table)?.get(column);
2642
+ if (cached !== void 0) return cached;
2643
+ const schema = this.schemaName || "public";
2644
+ const dataType = (await this.client.oneOrNone(`SELECT data_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 AND (column_name = $3 OR column_name = $4)`, [
2645
+ schema,
2646
+ table,
2647
+ column,
2648
+ column.toLowerCase()
2649
+ ]))?.data_type ?? null;
2650
+ if (dataType) {
2651
+ if (snapshot) {
2652
+ let snapshotTypes = snapshot.columnTypes.get(table);
2653
+ if (!snapshotTypes) {
2654
+ snapshotTypes = /* @__PURE__ */ new Map();
2655
+ snapshot.columnTypes.set(table, snapshotTypes);
2656
+ }
2657
+ snapshotTypes.set(column, dataType);
2658
+ }
2659
+ let types = this.columnTypeCache.get(table);
2660
+ if (!types) {
2661
+ types = /* @__PURE__ */ new Map();
2662
+ this.columnTypeCache.set(table, types);
2663
+ }
2664
+ types.set(column, dataType);
2665
+ }
2666
+ return dataType;
2667
+ }
2668
+ /**
2610
2669
  * Prepares values for insertion, handling JSONB columns by stringifying them
2611
2670
  */
2612
2671
  prepareValuesForInsert(record, tableName) {
@@ -2805,6 +2864,7 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
2805
2864
  }, error);
2806
2865
  } finally {
2807
2866
  this.tableColumnsCache.delete(tableName);
2867
+ this.columnTypeCache.delete(tableName);
2808
2868
  }
2809
2869
  }
2810
2870
  async setupTimestampTriggers(tableName) {
@@ -3127,6 +3187,7 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
3127
3187
  }, error);
3128
3188
  } finally {
3129
3189
  this.tableColumnsCache.delete(tableName);
3190
+ this.columnTypeCache.delete(tableName);
3130
3191
  }
3131
3192
  }
3132
3193
  async load({ tableName, keys }) {
@@ -3190,6 +3251,7 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
3190
3251
  }, error);
3191
3252
  } finally {
3192
3253
  this.tableColumnsCache.delete(tableName);
3254
+ this.columnTypeCache.delete(tableName);
3193
3255
  }
3194
3256
  }
3195
3257
  async createIndex(options) {
@@ -19322,6 +19384,29 @@ function getTableName({ indexName, schemaName }) {
19322
19384
  const quotedIndexName = `"${indexName}"`;
19323
19385
  return schemaName ? `${schemaName}.${quotedIndexName}` : quotedIndexName;
19324
19386
  }
19387
+ /** Base name (before any schema prefix) of the expression index backing the status filter. */
19388
+ const WORKFLOW_SNAPSHOT_STATUS_INDEX = "mastra_workflow_snapshot_name_status_createdat_idx";
19389
+ /**
19390
+ * Schema-prefixed name of the status index, lowercased and truncated the same way Postgres
19391
+ * stores it, so the init snapshot's index set answers "does it exist?" without a probe or a
19392
+ * no-op `CREATE INDEX` (schema-prefixed names routinely exceed the 63-byte limit).
19393
+ */
19394
+ function workflowSnapshotStatusIndexName(schemaName) {
19395
+ return buildConstraintName({
19396
+ baseName: WORKFLOW_SNAPSHOT_STATUS_INDEX,
19397
+ schemaName: schemaName && schemaName !== "public" ? schemaName : void 0
19398
+ });
19399
+ }
19400
+ /**
19401
+ * Expression index on `(workflow_name, snapshot->>'status', "createdAt" DESC)` so
19402
+ * listWorkflowRuns() status filters can use an index instead of scanning every snapshot.
19403
+ */
19404
+ function workflowSnapshotStatusIndexSQL(indexName, schemaName) {
19405
+ return `CREATE INDEX IF NOT EXISTS "${indexName}" ON ${getTableName({
19406
+ indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
19407
+ schemaName: getSchemaName(schemaName)
19408
+ })} (workflow_name, (snapshot ->> 'status'), "createdAt" DESC)`;
19409
+ }
19325
19410
  /**
19326
19411
  * Sanitizes JSON string for PostgreSQL jsonb:
19327
19412
  * - Removes problematic Unicode sequences:
@@ -19382,33 +19467,55 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
19382
19467
  updatedAt: new Date(row.updatedAtZ || row.updatedAt)
19383
19468
  };
19384
19469
  }
19470
+ static getDefaultIndexDefs(schemaPrefix) {
19471
+ return [{
19472
+ name: `${schemaPrefix}mastra_workflow_snapshot_name_createdat_idx`,
19473
+ table: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
19474
+ columns: ["workflow_name", "createdAt DESC"]
19475
+ }];
19476
+ }
19385
19477
  /**
19386
19478
  * Returns all DDL statements for this domain: table with unique constraint.
19387
19479
  * Used by exportSchemas to produce a complete, reproducible schema export.
19388
19480
  */
19389
19481
  static getExportDDL(schemaName) {
19390
19482
  const statements = [];
19483
+ const parsedSchema = schemaName ? (0, _mastra_core_utils.parseSqlIdentifier)(schemaName, "schema name") : "";
19484
+ const schemaPrefix = parsedSchema && parsedSchema !== "public" ? `${parsedSchema}_` : "";
19391
19485
  statements.push(generateTableSQL({
19392
19486
  tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
19393
19487
  schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT],
19394
19488
  schemaName,
19395
19489
  includeAllConstraints: true
19396
19490
  }));
19491
+ for (const idx of WorkflowsPG.getDefaultIndexDefs(schemaPrefix)) statements.push(generateIndexSQL(idx, schemaName));
19492
+ statements.push(`${workflowSnapshotStatusIndexSQL(workflowSnapshotStatusIndexName(parsedSchema), schemaName)};`);
19397
19493
  return statements;
19398
19494
  }
19399
19495
  /**
19400
19496
  * Returns default index definitions for the workflows domain tables.
19401
- * Currently no default indexes are defined for workflows.
19402
19497
  */
19403
19498
  getDefaultIndexDefinitions() {
19404
- return [];
19499
+ const schemaPrefix = this.#schema !== "public" ? `${this.#schema}_` : "";
19500
+ return WorkflowsPG.getDefaultIndexDefs(schemaPrefix);
19405
19501
  }
19406
19502
  /**
19407
19503
  * Creates default indexes for optimal query performance.
19408
- * Currently no default indexes are defined for workflows.
19409
19504
  */
19410
19505
  async createDefaultIndexes() {
19411
19506
  if (this.#skipDefaultIndexes) return;
19507
+ for (const indexDef of this.getDefaultIndexDefinitions()) try {
19508
+ await this.#db.createIndex(indexDef);
19509
+ } catch (error) {
19510
+ this.logger?.warn?.(`Failed to create index ${indexDef.name}:`, error);
19511
+ }
19512
+ if (await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "snapshot") !== "jsonb") return;
19513
+ const indexName = workflowSnapshotStatusIndexName(this.#schema);
19514
+ try {
19515
+ await this.#db.createIndexFromStatement(indexName, workflowSnapshotStatusIndexSQL(indexName, this.#schema));
19516
+ } catch (error) {
19517
+ this.logger?.warn?.(`Failed to create index ${indexName}:`, error);
19518
+ }
19412
19519
  }
19413
19520
  async init() {
19414
19521
  await this.#db.createTable({
@@ -19698,7 +19805,8 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
19698
19805
  paramIndex++;
19699
19806
  }
19700
19807
  if (status) {
19701
- conditions.push(`regexp_replace(snapshot::text, '\\\\u(0000|[Dd][89A-Fa-f][0-9A-Fa-f]{2})', '', 'g')::jsonb ->> 'status' = $${paramIndex}`);
19808
+ const statusExpr = await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "snapshot") === "jsonb" ? `snapshot ->> 'status'` : `regexp_replace(snapshot::text, '\\\\u(0000|[Dd][89A-Fa-f][0-9A-Fa-f]{2})', '', 'g')::jsonb ->> 'status'`;
19809
+ conditions.push(`${statusExpr} = $${paramIndex}`);
19702
19810
  values.push(status);
19703
19811
  paramIndex++;
19704
19812
  }