@mastra/pg 1.23.0-alpha.6 → 1.23.0-alpha.7
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 +136 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +136 -44
- package/dist/index.js.map +1 -1
- package/dist/vector/index.d.ts +9 -8
- package/dist/vector/index.d.ts.map +1 -1
- package/dist/vector/namespace-test-utils.d.ts +9 -0
- package/dist/vector/namespace-test-utils.d.ts.map +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -560,6 +560,8 @@ var PgVector = class extends MastraVector {
|
|
|
560
560
|
indexMetadataCache = /* @__PURE__ */ new Map();
|
|
561
561
|
createdIndexes = /* @__PURE__ */ new Map();
|
|
562
562
|
namespaceReadyIndexes = /* @__PURE__ */ new Set();
|
|
563
|
+
/** In-flight lazy namespace migrations, keyed by index name (see {@link ensureNamespaceReady}). */
|
|
564
|
+
namespaceReadyCache = /* @__PURE__ */ new Map();
|
|
563
565
|
indexVectorTypes = /* @__PURE__ */ new Map();
|
|
564
566
|
mutexesByName = /* @__PURE__ */ new Map();
|
|
565
567
|
schema;
|
|
@@ -804,8 +806,10 @@ var PgVector = class extends MastraVector {
|
|
|
804
806
|
AND attnum > 0
|
|
805
807
|
AND NOT attisdropped`, [tableName])).rowCount === 0) return;
|
|
806
808
|
await client.query(`ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS namespace VARCHAR(255) NOT NULL DEFAULT '${DEFAULT_NAMESPACE}'`);
|
|
807
|
-
|
|
808
|
-
|
|
809
|
+
if (!(await this.getNamespaceSchemaState(tableName, client)).composite_index) {
|
|
810
|
+
const namespaceIndexName = this.getNamespaceIndexName(parsedIndexName);
|
|
811
|
+
await client.query(`CREATE UNIQUE INDEX IF NOT EXISTS "${namespaceIndexName}" ON ${tableName} (namespace, vector_id)`);
|
|
812
|
+
}
|
|
809
813
|
const legacyConstraints = await client.query(`SELECT c.conname
|
|
810
814
|
FROM pg_constraint c
|
|
811
815
|
WHERE c.conrelid = to_regclass($1)
|
|
@@ -816,21 +820,84 @@ var PgVector = class extends MastraVector {
|
|
|
816
820
|
await client.query(`ALTER TABLE ${tableName} DROP CONSTRAINT "${parsedConstraintName}"`);
|
|
817
821
|
}
|
|
818
822
|
}
|
|
819
|
-
/**
|
|
820
|
-
* Name of the unique (namespace, vector_id) index for a table.
|
|
821
|
-
*
|
|
822
|
-
* The full `<index>_namespace_vector_id_idx` name is kept whenever it fits so tables that
|
|
823
|
-
* were already migrated keep matching `IF NOT EXISTS`. Longer index names would exceed
|
|
824
|
-
* Postgres' 63-char identifier limit, so those fall back to a truncated prefix plus a hash
|
|
825
|
-
* of the index name to distinguish tables that
|
|
826
|
-
* share a long prefix.
|
|
827
|
-
*/
|
|
828
823
|
getNamespaceIndexName(parsedIndexName) {
|
|
829
824
|
const fullName = `${parsedIndexName}_namespace_vector_id_idx`;
|
|
830
825
|
if (fullName.length <= 63) return fullName;
|
|
831
826
|
const suffix = `_ns_${createHash("sha256").update(parsedIndexName).digest("hex").slice(0, 32)}_idx`;
|
|
832
827
|
return `${parsedIndexName.slice(0, 63 - suffix.length)}${suffix}`;
|
|
833
828
|
}
|
|
829
|
+
async getNamespaceSchemaState(tableName, client) {
|
|
830
|
+
return (await client.query(`SELECT
|
|
831
|
+
EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = to_regclass($1)
|
|
832
|
+
AND attname = 'vector_id' AND attnum > 0 AND NOT attisdropped) AS vector_id,
|
|
833
|
+
EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = to_regclass($1)
|
|
834
|
+
AND attname = 'namespace' AND attnum > 0 AND NOT attisdropped) AS namespace,
|
|
835
|
+
EXISTS (
|
|
836
|
+
SELECT 1 FROM pg_index i
|
|
837
|
+
WHERE i.indrelid = to_regclass($1)
|
|
838
|
+
AND i.indisunique AND i.indisvalid AND i.indisready AND i.indimmediate
|
|
839
|
+
AND i.indpred IS NULL AND i.indexprs IS NULL AND i.indnkeyatts = 2
|
|
840
|
+
AND (SELECT array_agg(a.attname::text ORDER BY a.attname)
|
|
841
|
+
FROM unnest(i.indkey) WITH ORDINALITY AS k(attnum, position)
|
|
842
|
+
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = k.attnum
|
|
843
|
+
WHERE k.position <= i.indnkeyatts) = ARRAY['namespace', 'vector_id']
|
|
844
|
+
) AS composite_index,
|
|
845
|
+
EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = to_regclass($1)
|
|
846
|
+
AND contype = 'u' AND pg_get_constraintdef(oid) = 'UNIQUE (vector_id)') AS legacy_constraint`, [tableName])).rows[0];
|
|
847
|
+
}
|
|
848
|
+
async reconcileNamespace(indexName, client) {
|
|
849
|
+
const { tableName } = this.getTableName(indexName);
|
|
850
|
+
await client.query("BEGIN");
|
|
851
|
+
try {
|
|
852
|
+
await client.query("SELECT pg_advisory_xact_lock((1936876916::bigint << 32) | to_regclass($1)::oid::bigint)", [tableName]);
|
|
853
|
+
const state = await this.getNamespaceSchemaState(tableName, client);
|
|
854
|
+
if (!state.vector_id) {
|
|
855
|
+
await client.query("ROLLBACK");
|
|
856
|
+
return false;
|
|
857
|
+
}
|
|
858
|
+
if (!state.namespace || !state.composite_index || state.legacy_constraint) {
|
|
859
|
+
if (this.disableInit || process.env.MASTRA_DISABLE_STORAGE_INIT === "true") throw new MastraError({
|
|
860
|
+
id: createVectorErrorId("PG", "ENSURE_NAMESPACE", "MIGRATION_REQUIRED"),
|
|
861
|
+
domain: ErrorDomain.MASTRA_VECTOR,
|
|
862
|
+
category: ErrorCategory.USER,
|
|
863
|
+
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.`,
|
|
864
|
+
details: { indexName }
|
|
865
|
+
});
|
|
866
|
+
await this.ensureNamespaceSchema(indexName, client);
|
|
867
|
+
const migrated = await this.getNamespaceSchemaState(tableName, client);
|
|
868
|
+
if (!migrated.namespace || !migrated.composite_index || migrated.legacy_constraint) throw new MastraError({
|
|
869
|
+
id: createVectorErrorId("PG", "ENSURE_NAMESPACE", "MIGRATION_REQUIRED"),
|
|
870
|
+
domain: ErrorDomain.MASTRA_VECTOR,
|
|
871
|
+
category: ErrorCategory.USER,
|
|
872
|
+
text: `Vector index "${indexName}" requires a valid unique (namespace, vector_id) index. Resolve conflicting index names and retry the namespace migration.`,
|
|
873
|
+
details: { indexName }
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
await client.query("COMMIT");
|
|
877
|
+
return true;
|
|
878
|
+
} catch (error) {
|
|
879
|
+
await client.query("ROLLBACK");
|
|
880
|
+
throw error;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Every data path filters on `namespace`, but tables created before that column existed are
|
|
885
|
+
* only migrated by `createIndex()`, which callers are not required to invoke before reading.
|
|
886
|
+
* Run the migration lazily instead, once per index per process.
|
|
887
|
+
*/
|
|
888
|
+
ensureNamespaceReady(indexName) {
|
|
889
|
+
if (this.namespaceReadyIndexes.has(indexName)) return Promise.resolve();
|
|
890
|
+
return this.memoize(this.namespaceReadyCache, indexName, () => this.getMutexByName(`create-${indexName}`).runExclusive(async () => {
|
|
891
|
+
if (this.namespaceReadyIndexes.has(indexName)) return;
|
|
892
|
+
const client = await this.pool.connect();
|
|
893
|
+
try {
|
|
894
|
+
if (await this.reconcileNamespace(indexName, client)) this.namespaceReadyIndexes.add(indexName);
|
|
895
|
+
else this.namespaceReadyCache.delete(indexName);
|
|
896
|
+
} finally {
|
|
897
|
+
client.release();
|
|
898
|
+
}
|
|
899
|
+
}));
|
|
900
|
+
}
|
|
834
901
|
transformFilter(filter) {
|
|
835
902
|
return new PGFilterTranslator().translate(filter);
|
|
836
903
|
}
|
|
@@ -888,8 +955,10 @@ var PgVector = class extends MastraVector {
|
|
|
888
955
|
throw mastraError;
|
|
889
956
|
}
|
|
890
957
|
if (queryVector === void 0) {
|
|
891
|
-
|
|
958
|
+
let client;
|
|
892
959
|
try {
|
|
960
|
+
await this.ensureNamespaceReady(indexName);
|
|
961
|
+
client = await this.pool.connect();
|
|
893
962
|
const { sql: filterQuery, values: filterValues } = buildDeleteFilterQuery(this.transformFilter(filter));
|
|
894
963
|
const { tableName } = this.getTableName(indexName);
|
|
895
964
|
const filterClause = filterQuery.trim().replace(/^WHERE\s+/i, "");
|
|
@@ -915,6 +984,10 @@ var PgVector = class extends MastraVector {
|
|
|
915
984
|
...includeVector && embedding && { vector: JSON.parse(embedding) }
|
|
916
985
|
}));
|
|
917
986
|
} catch (error) {
|
|
987
|
+
if (error instanceof MastraError) {
|
|
988
|
+
this.logger?.trackException(error);
|
|
989
|
+
throw error;
|
|
990
|
+
}
|
|
918
991
|
const mastraError = new MastraError({
|
|
919
992
|
id: createVectorErrorId("PG", "QUERY", "FAILED"),
|
|
920
993
|
domain: ErrorDomain.MASTRA_VECTOR,
|
|
@@ -924,12 +997,14 @@ var PgVector = class extends MastraVector {
|
|
|
924
997
|
this.logger?.trackException(mastraError);
|
|
925
998
|
throw mastraError;
|
|
926
999
|
} finally {
|
|
927
|
-
client
|
|
1000
|
+
client?.release();
|
|
928
1001
|
}
|
|
929
1002
|
}
|
|
930
|
-
|
|
931
|
-
const client = await this.pool.connect();
|
|
1003
|
+
let client;
|
|
932
1004
|
try {
|
|
1005
|
+
await this.ensureNamespaceReady(indexName);
|
|
1006
|
+
const indexInfo = await this.getIndexMetadata({ indexName });
|
|
1007
|
+
client = await this.pool.connect();
|
|
933
1008
|
await this.ensureSearchPath(client);
|
|
934
1009
|
await client.query("BEGIN");
|
|
935
1010
|
const { sql: filterQuery, values: filterValues } = buildFilterQuery(this.transformFilter(filter), minScore, topK);
|
|
@@ -991,7 +1066,11 @@ var PgVector = class extends MastraVector {
|
|
|
991
1066
|
...includeVector && embedding && { vector: ops.parseEmbedding(embedding) }
|
|
992
1067
|
}));
|
|
993
1068
|
} catch (error) {
|
|
994
|
-
await client
|
|
1069
|
+
await client?.query("ROLLBACK");
|
|
1070
|
+
if (error instanceof MastraError) {
|
|
1071
|
+
this.logger?.trackException(error);
|
|
1072
|
+
throw error;
|
|
1073
|
+
}
|
|
995
1074
|
const mastraError = new MastraError({
|
|
996
1075
|
id: createVectorErrorId("PG", "QUERY", "FAILED"),
|
|
997
1076
|
domain: ErrorDomain.MASTRA_VECTOR,
|
|
@@ -1001,15 +1080,17 @@ var PgVector = class extends MastraVector {
|
|
|
1001
1080
|
this.logger?.trackException(mastraError);
|
|
1002
1081
|
throw mastraError;
|
|
1003
1082
|
} finally {
|
|
1004
|
-
client
|
|
1083
|
+
client?.release();
|
|
1005
1084
|
}
|
|
1006
1085
|
}
|
|
1007
1086
|
async upsert({ indexName, vectors, metadata, ids, deleteFilter, namespace = DEFAULT_NAMESPACE }) {
|
|
1008
1087
|
validateUpsertInput("PG", vectors, metadata, ids);
|
|
1009
1088
|
const { tableName } = this.getTableName(indexName);
|
|
1010
|
-
|
|
1011
|
-
const client = await this.pool.connect();
|
|
1089
|
+
let client;
|
|
1012
1090
|
try {
|
|
1091
|
+
const indexInfo = await this.getIndexMetadata({ indexName });
|
|
1092
|
+
await this.ensureNamespaceReady(indexName);
|
|
1093
|
+
client = await this.pool.connect();
|
|
1013
1094
|
await this.ensureSearchPath(client);
|
|
1014
1095
|
await client.query("BEGIN");
|
|
1015
1096
|
if (deleteFilter) {
|
|
@@ -1075,7 +1156,11 @@ var PgVector = class extends MastraVector {
|
|
|
1075
1156
|
});
|
|
1076
1157
|
return vectorIds;
|
|
1077
1158
|
} catch (error) {
|
|
1078
|
-
await client
|
|
1159
|
+
await client?.query("ROLLBACK");
|
|
1160
|
+
if (error instanceof MastraError) {
|
|
1161
|
+
this.logger?.trackException(error);
|
|
1162
|
+
throw error;
|
|
1163
|
+
}
|
|
1079
1164
|
if (error instanceof Error && error.message?.includes("expected") && error.message?.includes("dimensions")) {
|
|
1080
1165
|
const match = error.message.match(/expected (\d+) dimensions, not (\d+)/);
|
|
1081
1166
|
if (match) {
|
|
@@ -1104,7 +1189,7 @@ var PgVector = class extends MastraVector {
|
|
|
1104
1189
|
this.logger?.trackException(mastraError);
|
|
1105
1190
|
throw mastraError;
|
|
1106
1191
|
} finally {
|
|
1107
|
-
client
|
|
1192
|
+
client?.release();
|
|
1108
1193
|
}
|
|
1109
1194
|
}
|
|
1110
1195
|
hasher = xxhash();
|
|
@@ -1232,9 +1317,8 @@ var PgVector = class extends MastraVector {
|
|
|
1232
1317
|
namespace VARCHAR(255) NOT NULL DEFAULT '${DEFAULT_NAMESPACE}'
|
|
1233
1318
|
);
|
|
1234
1319
|
`);
|
|
1235
|
-
await this.
|
|
1320
|
+
if (await this.reconcileNamespace(indexName, client)) this.namespaceReadyIndexes.add(indexName);
|
|
1236
1321
|
this.createdIndexes.set(indexName, indexCacheKey);
|
|
1237
|
-
this.namespaceReadyIndexes.add(indexName);
|
|
1238
1322
|
this.indexVectorTypes.set(indexName, vectorType);
|
|
1239
1323
|
if (buildIndex) await this.setupIndex({
|
|
1240
1324
|
indexName,
|
|
@@ -1246,6 +1330,7 @@ var PgVector = class extends MastraVector {
|
|
|
1246
1330
|
} catch (error) {
|
|
1247
1331
|
this.createdIndexes.delete(indexName);
|
|
1248
1332
|
this.namespaceReadyIndexes.delete(indexName);
|
|
1333
|
+
this.namespaceReadyCache.delete(indexName);
|
|
1249
1334
|
this.indexVectorTypes.delete(indexName);
|
|
1250
1335
|
throw error;
|
|
1251
1336
|
} finally {
|
|
@@ -1620,27 +1705,30 @@ var PgVector = class extends MastraVector {
|
|
|
1620
1705
|
}
|
|
1621
1706
|
}
|
|
1622
1707
|
async deleteIndex({ indexName }) {
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1708
|
+
await this.getMutexByName(`create-${indexName}`).runExclusive(async () => {
|
|
1709
|
+
const client = await this.pool.connect();
|
|
1710
|
+
try {
|
|
1711
|
+
const { tableName } = this.getTableName(indexName);
|
|
1712
|
+
await client.query(`DROP TABLE IF EXISTS ${tableName} CASCADE`);
|
|
1713
|
+
this.createdIndexes.delete(indexName);
|
|
1714
|
+
this.namespaceReadyIndexes.delete(indexName);
|
|
1715
|
+
this.namespaceReadyCache.delete(indexName);
|
|
1716
|
+
this.indexVectorTypes.delete(indexName);
|
|
1717
|
+
this.invalidateIndexCaches(indexName);
|
|
1718
|
+
} catch (error) {
|
|
1719
|
+
await client.query("ROLLBACK");
|
|
1720
|
+
const mastraError = new MastraError({
|
|
1721
|
+
id: createVectorErrorId("PG", "DELETE_INDEX", "FAILED"),
|
|
1722
|
+
domain: ErrorDomain.MASTRA_VECTOR,
|
|
1723
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1724
|
+
details: { indexName }
|
|
1725
|
+
}, error);
|
|
1726
|
+
this.logger?.trackException(mastraError);
|
|
1727
|
+
throw mastraError;
|
|
1728
|
+
} finally {
|
|
1729
|
+
client.release();
|
|
1730
|
+
}
|
|
1731
|
+
});
|
|
1644
1732
|
}
|
|
1645
1733
|
async truncateIndex({ indexName }) {
|
|
1646
1734
|
const client = await this.pool.connect();
|
|
@@ -1696,6 +1784,7 @@ var PgVector = class extends MastraVector {
|
|
|
1696
1784
|
details: { indexName }
|
|
1697
1785
|
});
|
|
1698
1786
|
const indexInfo = await this.getIndexMetadata({ indexName });
|
|
1787
|
+
await this.ensureNamespaceReady(indexName);
|
|
1699
1788
|
client = await this.pool.connect();
|
|
1700
1789
|
await this.ensureSearchPath(client);
|
|
1701
1790
|
const { tableName } = this.getTableName(indexName);
|
|
@@ -1789,6 +1878,7 @@ var PgVector = class extends MastraVector {
|
|
|
1789
1878
|
async deleteVector({ indexName, id, namespace = DEFAULT_NAMESPACE }) {
|
|
1790
1879
|
let client;
|
|
1791
1880
|
try {
|
|
1881
|
+
await this.ensureNamespaceReady(indexName);
|
|
1792
1882
|
client = await this.pool.connect();
|
|
1793
1883
|
const { tableName } = this.getTableName(indexName);
|
|
1794
1884
|
const query = `
|
|
@@ -1797,6 +1887,7 @@ var PgVector = class extends MastraVector {
|
|
|
1797
1887
|
`;
|
|
1798
1888
|
await client.query(query, [id, namespace]);
|
|
1799
1889
|
} catch (error) {
|
|
1890
|
+
if (error instanceof MastraError) throw error;
|
|
1800
1891
|
const mastraError = new MastraError({
|
|
1801
1892
|
id: createVectorErrorId("PG", "DELETE_VECTOR", "FAILED"),
|
|
1802
1893
|
domain: ErrorDomain.MASTRA_VECTOR,
|
|
@@ -1823,6 +1914,7 @@ var PgVector = class extends MastraVector {
|
|
|
1823
1914
|
let client;
|
|
1824
1915
|
const effectiveNamespace = namespace ?? DEFAULT_NAMESPACE;
|
|
1825
1916
|
try {
|
|
1917
|
+
await this.ensureNamespaceReady(indexName);
|
|
1826
1918
|
client = await this.pool.connect();
|
|
1827
1919
|
const { tableName } = this.getTableName(indexName);
|
|
1828
1920
|
if (!filter && !ids && namespace === void 0) throw new MastraError({
|