@mastra/pg 1.23.0-alpha.6 → 1.23.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.
@@ -3,7 +3,7 @@ name: mastra-pg
3
3
  description: Documentation for @mastra/pg. Use when working with @mastra/pg APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/pg"
6
- version: "1.23.0-alpha.6"
6
+ version: "1.23.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.23.0-alpha.6",
2
+ "version": "1.23.0",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
package/dist/index.cjs CHANGED
@@ -584,6 +584,8 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
584
584
  indexMetadataCache = /* @__PURE__ */ new Map();
585
585
  createdIndexes = /* @__PURE__ */ new Map();
586
586
  namespaceReadyIndexes = /* @__PURE__ */ new Set();
587
+ /** In-flight lazy namespace migrations, keyed by index name (see {@link ensureNamespaceReady}). */
588
+ namespaceReadyCache = /* @__PURE__ */ new Map();
587
589
  indexVectorTypes = /* @__PURE__ */ new Map();
588
590
  mutexesByName = /* @__PURE__ */ new Map();
589
591
  schema;
@@ -828,8 +830,10 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
828
830
  AND attnum > 0
829
831
  AND NOT attisdropped`, [tableName])).rowCount === 0) return;
830
832
  await client.query(`ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS namespace VARCHAR(255) NOT NULL DEFAULT '${DEFAULT_NAMESPACE}'`);
831
- const namespaceIndexName = this.getNamespaceIndexName(parsedIndexName);
832
- await client.query(`CREATE UNIQUE INDEX IF NOT EXISTS "${namespaceIndexName}" ON ${tableName} (namespace, vector_id)`);
833
+ if (!(await this.getNamespaceSchemaState(tableName, client)).composite_index) {
834
+ const namespaceIndexName = this.getNamespaceIndexName(parsedIndexName);
835
+ await client.query(`CREATE UNIQUE INDEX IF NOT EXISTS "${namespaceIndexName}" ON ${tableName} (namespace, vector_id)`);
836
+ }
833
837
  const legacyConstraints = await client.query(`SELECT c.conname
834
838
  FROM pg_constraint c
835
839
  WHERE c.conrelid = to_regclass($1)
@@ -840,21 +844,84 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
840
844
  await client.query(`ALTER TABLE ${tableName} DROP CONSTRAINT "${parsedConstraintName}"`);
841
845
  }
842
846
  }
843
- /**
844
- * Name of the unique (namespace, vector_id) index for a table.
845
- *
846
- * The full `<index>_namespace_vector_id_idx` name is kept whenever it fits so tables that
847
- * were already migrated keep matching `IF NOT EXISTS`. Longer index names would exceed
848
- * Postgres' 63-char identifier limit, so those fall back to a truncated prefix plus a hash
849
- * of the index name to distinguish tables that
850
- * share a long prefix.
851
- */
852
847
  getNamespaceIndexName(parsedIndexName) {
853
848
  const fullName = `${parsedIndexName}_namespace_vector_id_idx`;
854
849
  if (fullName.length <= 63) return fullName;
855
850
  const suffix = `_ns_${(0, crypto$1.createHash)("sha256").update(parsedIndexName).digest("hex").slice(0, 32)}_idx`;
856
851
  return `${parsedIndexName.slice(0, 63 - suffix.length)}${suffix}`;
857
852
  }
853
+ async getNamespaceSchemaState(tableName, client) {
854
+ return (await client.query(`SELECT
855
+ EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = to_regclass($1)
856
+ AND attname = 'vector_id' AND attnum > 0 AND NOT attisdropped) AS vector_id,
857
+ EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = to_regclass($1)
858
+ AND attname = 'namespace' AND attnum > 0 AND NOT attisdropped) AS namespace,
859
+ EXISTS (
860
+ SELECT 1 FROM pg_index i
861
+ WHERE i.indrelid = to_regclass($1)
862
+ AND i.indisunique AND i.indisvalid AND i.indisready AND i.indimmediate
863
+ AND i.indpred IS NULL AND i.indexprs IS NULL AND i.indnkeyatts = 2
864
+ AND (SELECT array_agg(a.attname::text ORDER BY a.attname)
865
+ FROM unnest(i.indkey) WITH ORDINALITY AS k(attnum, position)
866
+ JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = k.attnum
867
+ WHERE k.position <= i.indnkeyatts) = ARRAY['namespace', 'vector_id']
868
+ ) AS composite_index,
869
+ EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = to_regclass($1)
870
+ AND contype = 'u' AND pg_get_constraintdef(oid) = 'UNIQUE (vector_id)') AS legacy_constraint`, [tableName])).rows[0];
871
+ }
872
+ async reconcileNamespace(indexName, client) {
873
+ const { tableName } = this.getTableName(indexName);
874
+ await client.query("BEGIN");
875
+ try {
876
+ await client.query("SELECT pg_advisory_xact_lock((1936876916::bigint << 32) | to_regclass($1)::oid::bigint)", [tableName]);
877
+ const state = await this.getNamespaceSchemaState(tableName, client);
878
+ if (!state.vector_id) {
879
+ await client.query("ROLLBACK");
880
+ return false;
881
+ }
882
+ if (!state.namespace || !state.composite_index || state.legacy_constraint) {
883
+ if (this.disableInit || process.env.MASTRA_DISABLE_STORAGE_INIT === "true") throw new _mastra_core_error.MastraError({
884
+ id: (0, _mastra_core_storage.createVectorErrorId)("PG", "ENSURE_NAMESPACE", "MIGRATION_REQUIRED"),
885
+ domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
886
+ category: _mastra_core_error.ErrorCategory.USER,
887
+ text: `Vector index "${indexName}" requires namespace migration and schema changes are disabled. Call createIndex({ indexName: "${indexName}", dimension }) with init enabled, or apply the namespace migration SQL from the @mastra/pg changelog.`,
888
+ details: { indexName }
889
+ });
890
+ await this.ensureNamespaceSchema(indexName, client);
891
+ const migrated = await this.getNamespaceSchemaState(tableName, client);
892
+ if (!migrated.namespace || !migrated.composite_index || migrated.legacy_constraint) throw new _mastra_core_error.MastraError({
893
+ id: (0, _mastra_core_storage.createVectorErrorId)("PG", "ENSURE_NAMESPACE", "MIGRATION_REQUIRED"),
894
+ domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
895
+ category: _mastra_core_error.ErrorCategory.USER,
896
+ text: `Vector index "${indexName}" requires a valid unique (namespace, vector_id) index. Resolve conflicting index names and retry the namespace migration.`,
897
+ details: { indexName }
898
+ });
899
+ }
900
+ await client.query("COMMIT");
901
+ return true;
902
+ } catch (error) {
903
+ await client.query("ROLLBACK");
904
+ throw error;
905
+ }
906
+ }
907
+ /**
908
+ * Every data path filters on `namespace`, but tables created before that column existed are
909
+ * only migrated by `createIndex()`, which callers are not required to invoke before reading.
910
+ * Run the migration lazily instead, once per index per process.
911
+ */
912
+ ensureNamespaceReady(indexName) {
913
+ if (this.namespaceReadyIndexes.has(indexName)) return Promise.resolve();
914
+ return this.memoize(this.namespaceReadyCache, indexName, () => this.getMutexByName(`create-${indexName}`).runExclusive(async () => {
915
+ if (this.namespaceReadyIndexes.has(indexName)) return;
916
+ const client = await this.pool.connect();
917
+ try {
918
+ if (await this.reconcileNamespace(indexName, client)) this.namespaceReadyIndexes.add(indexName);
919
+ else this.namespaceReadyCache.delete(indexName);
920
+ } finally {
921
+ client.release();
922
+ }
923
+ }));
924
+ }
858
925
  transformFilter(filter) {
859
926
  return new PGFilterTranslator().translate(filter);
860
927
  }
@@ -912,8 +979,10 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
912
979
  throw mastraError;
913
980
  }
914
981
  if (queryVector === void 0) {
915
- const client = await this.pool.connect();
982
+ let client;
916
983
  try {
984
+ await this.ensureNamespaceReady(indexName);
985
+ client = await this.pool.connect();
917
986
  const { sql: filterQuery, values: filterValues } = buildDeleteFilterQuery(this.transformFilter(filter));
918
987
  const { tableName } = this.getTableName(indexName);
919
988
  const filterClause = filterQuery.trim().replace(/^WHERE\s+/i, "");
@@ -939,6 +1008,10 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
939
1008
  ...includeVector && embedding && { vector: JSON.parse(embedding) }
940
1009
  }));
941
1010
  } catch (error) {
1011
+ if (error instanceof _mastra_core_error.MastraError) {
1012
+ this.logger?.trackException(error);
1013
+ throw error;
1014
+ }
942
1015
  const mastraError = new _mastra_core_error.MastraError({
943
1016
  id: (0, _mastra_core_storage.createVectorErrorId)("PG", "QUERY", "FAILED"),
944
1017
  domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
@@ -948,12 +1021,14 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
948
1021
  this.logger?.trackException(mastraError);
949
1022
  throw mastraError;
950
1023
  } finally {
951
- client.release();
1024
+ client?.release();
952
1025
  }
953
1026
  }
954
- const indexInfo = await this.getIndexMetadata({ indexName });
955
- const client = await this.pool.connect();
1027
+ let client;
956
1028
  try {
1029
+ await this.ensureNamespaceReady(indexName);
1030
+ const indexInfo = await this.getIndexMetadata({ indexName });
1031
+ client = await this.pool.connect();
957
1032
  await this.ensureSearchPath(client);
958
1033
  await client.query("BEGIN");
959
1034
  const { sql: filterQuery, values: filterValues } = buildFilterQuery(this.transformFilter(filter), minScore, topK);
@@ -1015,7 +1090,11 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1015
1090
  ...includeVector && embedding && { vector: ops.parseEmbedding(embedding) }
1016
1091
  }));
1017
1092
  } catch (error) {
1018
- await client.query("ROLLBACK");
1093
+ await client?.query("ROLLBACK");
1094
+ if (error instanceof _mastra_core_error.MastraError) {
1095
+ this.logger?.trackException(error);
1096
+ throw error;
1097
+ }
1019
1098
  const mastraError = new _mastra_core_error.MastraError({
1020
1099
  id: (0, _mastra_core_storage.createVectorErrorId)("PG", "QUERY", "FAILED"),
1021
1100
  domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
@@ -1025,15 +1104,17 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1025
1104
  this.logger?.trackException(mastraError);
1026
1105
  throw mastraError;
1027
1106
  } finally {
1028
- client.release();
1107
+ client?.release();
1029
1108
  }
1030
1109
  }
1031
1110
  async upsert({ indexName, vectors, metadata, ids, deleteFilter, namespace = DEFAULT_NAMESPACE }) {
1032
1111
  (0, _mastra_core_vector.validateUpsertInput)("PG", vectors, metadata, ids);
1033
1112
  const { tableName } = this.getTableName(indexName);
1034
- const indexInfo = await this.getIndexMetadata({ indexName });
1035
- const client = await this.pool.connect();
1113
+ let client;
1036
1114
  try {
1115
+ const indexInfo = await this.getIndexMetadata({ indexName });
1116
+ await this.ensureNamespaceReady(indexName);
1117
+ client = await this.pool.connect();
1037
1118
  await this.ensureSearchPath(client);
1038
1119
  await client.query("BEGIN");
1039
1120
  if (deleteFilter) {
@@ -1099,7 +1180,11 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1099
1180
  });
1100
1181
  return vectorIds;
1101
1182
  } catch (error) {
1102
- await client.query("ROLLBACK");
1183
+ await client?.query("ROLLBACK");
1184
+ if (error instanceof _mastra_core_error.MastraError) {
1185
+ this.logger?.trackException(error);
1186
+ throw error;
1187
+ }
1103
1188
  if (error instanceof Error && error.message?.includes("expected") && error.message?.includes("dimensions")) {
1104
1189
  const match = error.message.match(/expected (\d+) dimensions, not (\d+)/);
1105
1190
  if (match) {
@@ -1128,7 +1213,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1128
1213
  this.logger?.trackException(mastraError);
1129
1214
  throw mastraError;
1130
1215
  } finally {
1131
- client.release();
1216
+ client?.release();
1132
1217
  }
1133
1218
  }
1134
1219
  hasher = (0, xxhash_wasm.default)();
@@ -1256,9 +1341,8 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1256
1341
  namespace VARCHAR(255) NOT NULL DEFAULT '${DEFAULT_NAMESPACE}'
1257
1342
  );
1258
1343
  `);
1259
- await this.ensureNamespaceSchema(indexName, client);
1344
+ if (await this.reconcileNamespace(indexName, client)) this.namespaceReadyIndexes.add(indexName);
1260
1345
  this.createdIndexes.set(indexName, indexCacheKey);
1261
- this.namespaceReadyIndexes.add(indexName);
1262
1346
  this.indexVectorTypes.set(indexName, vectorType);
1263
1347
  if (buildIndex) await this.setupIndex({
1264
1348
  indexName,
@@ -1270,6 +1354,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1270
1354
  } catch (error) {
1271
1355
  this.createdIndexes.delete(indexName);
1272
1356
  this.namespaceReadyIndexes.delete(indexName);
1357
+ this.namespaceReadyCache.delete(indexName);
1273
1358
  this.indexVectorTypes.delete(indexName);
1274
1359
  throw error;
1275
1360
  } finally {
@@ -1644,27 +1729,30 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1644
1729
  }
1645
1730
  }
1646
1731
  async deleteIndex({ indexName }) {
1647
- const client = await this.pool.connect();
1648
- try {
1649
- const { tableName } = this.getTableName(indexName);
1650
- await client.query(`DROP TABLE IF EXISTS ${tableName} CASCADE`);
1651
- this.createdIndexes.delete(indexName);
1652
- this.namespaceReadyIndexes.delete(indexName);
1653
- this.indexVectorTypes.delete(indexName);
1654
- this.invalidateIndexCaches(indexName);
1655
- } catch (error) {
1656
- await client.query("ROLLBACK");
1657
- const mastraError = new _mastra_core_error.MastraError({
1658
- id: (0, _mastra_core_storage.createVectorErrorId)("PG", "DELETE_INDEX", "FAILED"),
1659
- domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
1660
- category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
1661
- details: { indexName }
1662
- }, error);
1663
- this.logger?.trackException(mastraError);
1664
- throw mastraError;
1665
- } finally {
1666
- client.release();
1667
- }
1732
+ await this.getMutexByName(`create-${indexName}`).runExclusive(async () => {
1733
+ const client = await this.pool.connect();
1734
+ try {
1735
+ const { tableName } = this.getTableName(indexName);
1736
+ await client.query(`DROP TABLE IF EXISTS ${tableName} CASCADE`);
1737
+ this.createdIndexes.delete(indexName);
1738
+ this.namespaceReadyIndexes.delete(indexName);
1739
+ this.namespaceReadyCache.delete(indexName);
1740
+ this.indexVectorTypes.delete(indexName);
1741
+ this.invalidateIndexCaches(indexName);
1742
+ } catch (error) {
1743
+ await client.query("ROLLBACK");
1744
+ const mastraError = new _mastra_core_error.MastraError({
1745
+ id: (0, _mastra_core_storage.createVectorErrorId)("PG", "DELETE_INDEX", "FAILED"),
1746
+ domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
1747
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
1748
+ details: { indexName }
1749
+ }, error);
1750
+ this.logger?.trackException(mastraError);
1751
+ throw mastraError;
1752
+ } finally {
1753
+ client.release();
1754
+ }
1755
+ });
1668
1756
  }
1669
1757
  async truncateIndex({ indexName }) {
1670
1758
  const client = await this.pool.connect();
@@ -1720,6 +1808,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1720
1808
  details: { indexName }
1721
1809
  });
1722
1810
  const indexInfo = await this.getIndexMetadata({ indexName });
1811
+ await this.ensureNamespaceReady(indexName);
1723
1812
  client = await this.pool.connect();
1724
1813
  await this.ensureSearchPath(client);
1725
1814
  const { tableName } = this.getTableName(indexName);
@@ -1813,6 +1902,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1813
1902
  async deleteVector({ indexName, id, namespace = DEFAULT_NAMESPACE }) {
1814
1903
  let client;
1815
1904
  try {
1905
+ await this.ensureNamespaceReady(indexName);
1816
1906
  client = await this.pool.connect();
1817
1907
  const { tableName } = this.getTableName(indexName);
1818
1908
  const query = `
@@ -1821,6 +1911,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1821
1911
  `;
1822
1912
  await client.query(query, [id, namespace]);
1823
1913
  } catch (error) {
1914
+ if (error instanceof _mastra_core_error.MastraError) throw error;
1824
1915
  const mastraError = new _mastra_core_error.MastraError({
1825
1916
  id: (0, _mastra_core_storage.createVectorErrorId)("PG", "DELETE_VECTOR", "FAILED"),
1826
1917
  domain: _mastra_core_error.ErrorDomain.MASTRA_VECTOR,
@@ -1847,6 +1938,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
1847
1938
  let client;
1848
1939
  const effectiveNamespace = namespace ?? DEFAULT_NAMESPACE;
1849
1940
  try {
1941
+ await this.ensureNamespaceReady(indexName);
1850
1942
  client = await this.pool.connect();
1851
1943
  const { tableName } = this.getTableName(indexName);
1852
1944
  if (!filter && !ids && namespace === void 0) throw new _mastra_core_error.MastraError({