@mastra/pg 1.30.0 → 1.31.0-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/docs/references/docs-memory-semantic-recall.md +2 -2
- package/dist/docs/references/docs-memory-working-memory.md +16 -0
- package/dist/docs/references/reference-memory-memory-class.md +2 -2
- package/dist/docs/references/reference-processors-message-history-processor.md +3 -2
- package/dist/docs/references/reference-processors-semantic-recall-processor.md +2 -3
- package/dist/docs/references/reference-processors-working-memory-processor.md +3 -2
- package/dist/docs/references/reference-rag-metadata-filters.md +1 -1
- package/dist/docs/references/reference-rag-vector-databases.md +1 -1
- package/dist/index.cjs +147 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +147 -25
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts +9 -0
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/db/schema-snapshot.d.ts +7 -1
- package/dist/storage/db/schema-snapshot.d.ts.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +5 -0
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/docs/SKILL.md
CHANGED
|
@@ -411,11 +411,11 @@ PostgreSQL supports both IVFFlat and HNSW indexes. By default, Mastra creates an
|
|
|
411
411
|
|
|
412
412
|
```typescript
|
|
413
413
|
import { Memory } from '@mastra/memory'
|
|
414
|
-
import {
|
|
414
|
+
import { PostgresStore, PgVector } from '@mastra/pg'
|
|
415
415
|
|
|
416
416
|
const agent = new Agent({
|
|
417
417
|
memory: new Memory({
|
|
418
|
-
storage: new
|
|
418
|
+
storage: new PostgresStore({
|
|
419
419
|
id: 'agent-storage',
|
|
420
420
|
connectionString: process.env.DATABASE_URL,
|
|
421
421
|
}),
|
|
@@ -272,6 +272,22 @@ Schema-based working memory uses **merge semantics**, meaning the agent only nee
|
|
|
272
272
|
- **Set a field to `null` to delete it:** This explicitly removes the field from memory
|
|
273
273
|
- **Arrays are replaced entirely:** When an array field is provided, it replaces the existing array (arrays aren't merged element-by-element)
|
|
274
274
|
|
|
275
|
+
### Concurrent updates
|
|
276
|
+
|
|
277
|
+
With `scope: 'resource'` and a storage adapter that supports atomic merges (currently `@mastra/pg`), each merge runs inside a row-locked database transaction. Concurrent updates to different fields from separate processes are all kept. Other adapters merge in the application, which only serializes writers inside a single process.
|
|
278
|
+
|
|
279
|
+
To merge a partial update from your own code, call `memory.mergeWorkingMemory()`:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
await memory.mergeWorkingMemory({
|
|
283
|
+
threadId: 'thread-1',
|
|
284
|
+
resourceId: 'user-123',
|
|
285
|
+
workingMemory: { preferences: { tone: 'formal' } },
|
|
286
|
+
})
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
It throws if working memory isn't schema-based and resource-scoped, or if the storage adapter doesn't support atomic merges. Check `await memory.supportsAtomicWorkingMemoryMerge()` first if you need to fall back.
|
|
290
|
+
|
|
275
291
|
## Choosing between template and schema
|
|
276
292
|
|
|
277
293
|
- Use a **template** (Markdown) if you want the agent to maintain memory as a free-form text block, such as a user profile or scratchpad. Templates use **replace semantics**: the agent must provide the complete memory content on each update.
|
|
@@ -101,14 +101,14 @@ export const agent = new Agent({
|
|
|
101
101
|
import { Memory } from '@mastra/memory'
|
|
102
102
|
import { Agent } from '@mastra/core/agent'
|
|
103
103
|
import { ModelRouterEmbeddingModel } from '@mastra/core/llm'
|
|
104
|
-
import {
|
|
104
|
+
import { PostgresStore, PgVector } from '@mastra/pg'
|
|
105
105
|
|
|
106
106
|
export const agent = new Agent({
|
|
107
107
|
name: 'pg-agent',
|
|
108
108
|
instructions: 'You are an agent with optimized PostgreSQL memory.',
|
|
109
109
|
model: 'openai/gpt-5.6-sol',
|
|
110
110
|
memory: new Memory({
|
|
111
|
-
storage: new
|
|
111
|
+
storage: new PostgresStore({
|
|
112
112
|
id: 'pg-agent-storage',
|
|
113
113
|
connectionString: process.env.DATABASE_URL,
|
|
114
114
|
}),
|
|
@@ -40,9 +40,10 @@ const processor = new MessageHistory({
|
|
|
40
40
|
```typescript
|
|
41
41
|
import { Agent } from '@mastra/core/agent'
|
|
42
42
|
import { MessageHistory } from '@mastra/core/processors'
|
|
43
|
-
import {
|
|
43
|
+
import { PostgresStore } from '@mastra/pg'
|
|
44
44
|
|
|
45
|
-
const storage = new
|
|
45
|
+
const storage = new PostgresStore({
|
|
46
|
+
id: 'pg-storage',
|
|
46
47
|
connectionString: process.env.DATABASE_URL,
|
|
47
48
|
})
|
|
48
49
|
|
|
@@ -59,11 +59,10 @@ const processor = new SemanticRecall({
|
|
|
59
59
|
```typescript
|
|
60
60
|
import { Agent } from '@mastra/core/agent'
|
|
61
61
|
import { SemanticRecall, MessageHistory } from '@mastra/core/processors'
|
|
62
|
-
import {
|
|
63
|
-
import { PgVector } from '@mastra/pg'
|
|
62
|
+
import { PostgresStore, PgVector } from '@mastra/pg'
|
|
64
63
|
import { openai } from '@ai-sdk/openai'
|
|
65
64
|
|
|
66
|
-
const storage = new
|
|
65
|
+
const storage = new PostgresStore({
|
|
67
66
|
id: 'pg-storage',
|
|
68
67
|
connectionString: process.env.DATABASE_URL,
|
|
69
68
|
})
|
|
@@ -62,9 +62,10 @@ const processor = new WorkingMemory({
|
|
|
62
62
|
```typescript
|
|
63
63
|
import { Agent } from '@mastra/core/agent'
|
|
64
64
|
import { WorkingMemory, MessageHistory } from '@mastra/core/processors'
|
|
65
|
-
import {
|
|
65
|
+
import { PostgresStore } from '@mastra/pg'
|
|
66
66
|
|
|
67
|
-
const storage = new
|
|
67
|
+
const storage = new PostgresStore({
|
|
68
|
+
id: 'pg-storage',
|
|
68
69
|
connectionString: process.env.DATABASE_URL,
|
|
69
70
|
})
|
|
70
71
|
|
|
@@ -231,7 +231,7 @@ await store.createIndex({
|
|
|
231
231
|
- [libSQL](https://mastra.ai/reference/vectors/libsql)
|
|
232
232
|
- [MongoDB](https://mastra.ai/reference/vectors/mongodb)
|
|
233
233
|
- [OracleDB](https://mastra.ai/reference/vectors/oracledb)
|
|
234
|
-
- [
|
|
234
|
+
- [PgVector](https://mastra.ai/reference/vectors/pg)
|
|
235
235
|
- [Pinecone](https://mastra.ai/reference/vectors/pinecone)
|
|
236
236
|
- [Qdrant](https://mastra.ai/reference/vectors/qdrant)
|
|
237
237
|
- [Upstash](https://mastra.ai/reference/vectors/upstash)
|
package/dist/index.cjs
CHANGED
|
@@ -2507,7 +2507,7 @@ async function loadSchemaSnapshot(client, schemaName) {
|
|
|
2507
2507
|
AND c.relkind IN ('r', 'p')
|
|
2508
2508
|
AND a.attnum > 0
|
|
2509
2509
|
AND NOT a.attisdropped`, [schema]),
|
|
2510
|
-
client.manyOrNone(`SELECT c.relname AS indexname, i.indisreplident AS is_replica_identity, i.indisprimary AS is_primary
|
|
2510
|
+
client.manyOrNone(`SELECT c.relname AS indexname, i.indisreplident AS is_replica_identity, i.indisprimary AS is_primary, i.indisvalid AS is_valid
|
|
2511
2511
|
FROM pg_catalog.pg_index i
|
|
2512
2512
|
JOIN pg_catalog.pg_class c ON c.oid = i.indexrelid
|
|
2513
2513
|
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
@@ -2532,8 +2532,10 @@ async function loadSchemaSnapshot(client, schemaName) {
|
|
|
2532
2532
|
const indexes = /* @__PURE__ */ new Set();
|
|
2533
2533
|
const replicaIdentityIndexes = /* @__PURE__ */ new Set();
|
|
2534
2534
|
const primaryKeyIndexes = /* @__PURE__ */ new Set();
|
|
2535
|
+
const invalidIndexes = /* @__PURE__ */ new Set();
|
|
2535
2536
|
for (const row of indexRows) {
|
|
2536
|
-
indexes.add(row.indexname);
|
|
2537
|
+
if (row.is_valid) indexes.add(row.indexname);
|
|
2538
|
+
else invalidIndexes.add(row.indexname);
|
|
2537
2539
|
if (row.is_replica_identity) replicaIdentityIndexes.add(row.indexname);
|
|
2538
2540
|
if (row.is_primary) primaryKeyIndexes.add(row.indexname.toLowerCase());
|
|
2539
2541
|
}
|
|
@@ -2544,7 +2546,8 @@ async function loadSchemaSnapshot(client, schemaName) {
|
|
|
2544
2546
|
columnTypes,
|
|
2545
2547
|
indexes,
|
|
2546
2548
|
replicaIdentityIndexes,
|
|
2547
|
-
primaryKeyIndexes
|
|
2549
|
+
primaryKeyIndexes,
|
|
2550
|
+
invalidIndexes
|
|
2548
2551
|
};
|
|
2549
2552
|
}
|
|
2550
2553
|
//#endregion
|
|
@@ -3692,11 +3695,8 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
3692
3695
|
schemaName: getSchemaName$6(this.schemaName)
|
|
3693
3696
|
});
|
|
3694
3697
|
const snapshot = this.schemaSnapshot;
|
|
3695
|
-
if (snapshot)
|
|
3696
|
-
|
|
3697
|
-
} else if (await this.client.oneOrNone(`SELECT 1 FROM pg_indexes
|
|
3698
|
-
WHERE indexname = $1
|
|
3699
|
-
AND schemaname = $2`, [name, schemaName])) return;
|
|
3698
|
+
if (snapshot?.indexes.has(name)) return;
|
|
3699
|
+
if (!await this.prepareIndexBuild(name, schemaName)) return;
|
|
3700
3700
|
const uniqueStr = unique ? "UNIQUE " : "";
|
|
3701
3701
|
const concurrentStr = concurrent ? "CONCURRENTLY " : "";
|
|
3702
3702
|
const methodStr = method !== "btree" ? `USING ${method} ` : "";
|
|
@@ -3715,16 +3715,9 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
3715
3715
|
let withStr = "";
|
|
3716
3716
|
if (storage && Object.keys(storage).length > 0) withStr = ` WITH (${Object.entries(storage).map(([key, value]) => `${key} = ${value}`).join(", ")})`;
|
|
3717
3717
|
const sql = `CREATE ${uniqueStr}INDEX ${concurrentStr}${`"${(0, _mastra_core_utils.parseSqlIdentifier)(name, "index name")}"`} ON ${fullTableName} ${methodStr}(${columnsStr})${withStr}${tablespaceStr}${whereStr}`;
|
|
3718
|
-
await this.
|
|
3718
|
+
await this.timedIndexBuild(name, sql);
|
|
3719
3719
|
snapshot?.indexes.add(name);
|
|
3720
3720
|
} catch (error) {
|
|
3721
|
-
if (error instanceof Error && error.message.includes("CONCURRENTLY")) {
|
|
3722
|
-
const retryOptions = {
|
|
3723
|
-
...options,
|
|
3724
|
-
concurrent: false
|
|
3725
|
-
};
|
|
3726
|
-
return this.createIndex(retryOptions);
|
|
3727
|
-
}
|
|
3728
3721
|
throw new _mastra_core_error.MastraError({
|
|
3729
3722
|
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "INDEX_CREATE", "FAILED"),
|
|
3730
3723
|
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
@@ -3748,9 +3741,43 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
|
|
|
3748
3741
|
async createIndexFromStatement(indexName, sql) {
|
|
3749
3742
|
const snapshot = this.schemaSnapshot;
|
|
3750
3743
|
if (snapshot?.indexes.has(indexName)) return;
|
|
3751
|
-
await this.
|
|
3744
|
+
if (!await this.prepareIndexBuild(indexName, this.schemaName || "public")) return;
|
|
3745
|
+
await this.timedIndexBuild(indexName, sql);
|
|
3752
3746
|
snapshot?.indexes.add(indexName);
|
|
3753
3747
|
}
|
|
3748
|
+
/**
|
|
3749
|
+
* Decides whether `indexName` still needs building. Returns false when a valid
|
|
3750
|
+
* index already exists, or when another session is currently building it.
|
|
3751
|
+
* An invalid index left by an interrupted `CREATE INDEX CONCURRENTLY` is
|
|
3752
|
+
* dropped (concurrently) so the caller rebuilds it — `IF NOT EXISTS` would
|
|
3753
|
+
* otherwise keep the broken index forever.
|
|
3754
|
+
*/
|
|
3755
|
+
async prepareIndexBuild(indexName, schemaName) {
|
|
3756
|
+
const snapshot = this.schemaSnapshot;
|
|
3757
|
+
if (snapshot && !snapshot.invalidIndexes.has(indexName)) return true;
|
|
3758
|
+
const existing = await this.client.oneOrNone(`SELECT i.indisvalid AS is_valid,
|
|
3759
|
+
EXISTS (SELECT 1 FROM pg_stat_progress_create_index p WHERE p.index_relid = c.oid) AS building
|
|
3760
|
+
FROM pg_catalog.pg_index i
|
|
3761
|
+
JOIN pg_catalog.pg_class c ON c.oid = i.indexrelid
|
|
3762
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
3763
|
+
WHERE c.relname = $1 AND n.nspname = $2`, [indexName, schemaName]);
|
|
3764
|
+
if (!existing) return true;
|
|
3765
|
+
if (existing.is_valid) return false;
|
|
3766
|
+
if (existing.building) {
|
|
3767
|
+
this.logger.warn(`Index ${indexName} is being built by another session; skipping`);
|
|
3768
|
+
return false;
|
|
3769
|
+
}
|
|
3770
|
+
this.logger.warn(`Dropping invalid index ${indexName} left by an interrupted build; rebuilding it`);
|
|
3771
|
+
const quotedIndexName = `"${(0, _mastra_core_utils.parseSqlIdentifier)(indexName, "index name")}"`;
|
|
3772
|
+
await this.client.none(`DROP INDEX CONCURRENTLY IF EXISTS ${getSchemaName$6(this.schemaName)}.${quotedIndexName}`);
|
|
3773
|
+
return true;
|
|
3774
|
+
}
|
|
3775
|
+
async timedIndexBuild(indexName, sql) {
|
|
3776
|
+
const startedAt = Date.now();
|
|
3777
|
+
this.logger.info(`Creating index ${indexName}`);
|
|
3778
|
+
await this.client.none(sql);
|
|
3779
|
+
this.logger.info(`Created index ${indexName} in ${Date.now() - startedAt}ms`);
|
|
3780
|
+
}
|
|
3754
3781
|
async dropIndex(indexName) {
|
|
3755
3782
|
try {
|
|
3756
3783
|
const schemaName = this.schemaName || "public";
|
|
@@ -10495,6 +10522,7 @@ function dedupeMessagesForSave(messages) {
|
|
|
10495
10522
|
var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
10496
10523
|
supportsPartialThreadUpdate = true;
|
|
10497
10524
|
supportsObservationalMemory = true;
|
|
10525
|
+
supportsAtomicWorkingMemoryMerge = true;
|
|
10498
10526
|
supportsObservationalMemoryHistorySearch = true;
|
|
10499
10527
|
/**
|
|
10500
10528
|
* Retention-eligible tables. `threads`, `messages`, and `resources` all anchor
|
|
@@ -11841,6 +11869,57 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
11841
11869
|
metadata: metadataJson ? JSON.parse(metadataJson) : resource.metadata
|
|
11842
11870
|
};
|
|
11843
11871
|
}
|
|
11872
|
+
async mergeResourceWorkingMemory({ resourceId, merge }) {
|
|
11873
|
+
const tableName = getTableName$3({
|
|
11874
|
+
indexName: _mastra_core_storage.TABLE_RESOURCES,
|
|
11875
|
+
schemaName: getSchemaName$3(this.#schema)
|
|
11876
|
+
});
|
|
11877
|
+
const callbackError = {};
|
|
11878
|
+
try {
|
|
11879
|
+
return await this.#db.client.tx(async (t) => {
|
|
11880
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11881
|
+
await t.none(`INSERT INTO ${tableName} (id, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ")
|
|
11882
|
+
VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (id) DO NOTHING`, [
|
|
11883
|
+
resourceId,
|
|
11884
|
+
toPgJson({}),
|
|
11885
|
+
now,
|
|
11886
|
+
now,
|
|
11887
|
+
now,
|
|
11888
|
+
now
|
|
11889
|
+
]);
|
|
11890
|
+
await t.one(`SELECT id FROM ${tableName} WHERE id = $1 FOR UPDATE`, [resourceId]);
|
|
11891
|
+
const existing = await this.#getResourceById(t, resourceId);
|
|
11892
|
+
let workingMemory;
|
|
11893
|
+
try {
|
|
11894
|
+
workingMemory = merge(existing.workingMemory ?? void 0);
|
|
11895
|
+
} catch (error) {
|
|
11896
|
+
callbackError.error = error;
|
|
11897
|
+
throw error;
|
|
11898
|
+
}
|
|
11899
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
11900
|
+
const updatedAtStr = updatedAt.toISOString();
|
|
11901
|
+
await t.none(`UPDATE ${tableName} SET "workingMemory" = $1, "updatedAt" = $2, "updatedAtZ" = $3 WHERE id = $4`, [
|
|
11902
|
+
workingMemory,
|
|
11903
|
+
updatedAtStr,
|
|
11904
|
+
updatedAtStr,
|
|
11905
|
+
resourceId
|
|
11906
|
+
]);
|
|
11907
|
+
return {
|
|
11908
|
+
...existing,
|
|
11909
|
+
workingMemory,
|
|
11910
|
+
updatedAt
|
|
11911
|
+
};
|
|
11912
|
+
});
|
|
11913
|
+
} catch (error) {
|
|
11914
|
+
if (error === callbackError.error || error instanceof _mastra_core_error.MastraError) throw error;
|
|
11915
|
+
throw new _mastra_core_error.MastraError({
|
|
11916
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "MERGE_RESOURCE_WORKING_MEMORY", "FAILED"),
|
|
11917
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
11918
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
11919
|
+
details: { resourceId }
|
|
11920
|
+
}, error);
|
|
11921
|
+
}
|
|
11922
|
+
}
|
|
11844
11923
|
async updateResource({ resourceId, workingMemory, metadata }) {
|
|
11845
11924
|
const existingResource = await this.#getResourceById(this.#db.client, resourceId);
|
|
11846
11925
|
if (!existingResource) {
|
|
@@ -17540,6 +17619,35 @@ function isMetadataField(field) {
|
|
|
17540
17619
|
function placeholders(values, offset) {
|
|
17541
17620
|
return values.map((_, index) => `$${offset + index}`).join(", ");
|
|
17542
17621
|
}
|
|
17622
|
+
/**
|
|
17623
|
+
* Unicode letters, marks, and digits as PostgreSQL bracket ranges, so `[^<ranges>]` splits words
|
|
17624
|
+
* like the planner's `[^\p{L}\p{M}\p{N}]`. PostgreSQL regexes have no `\p{…}` classes, and
|
|
17625
|
+
* `[[:alnum:]]` depends on the locale: it leaves out digits such as `² ½ ₂ ①`, and glibc files
|
|
17626
|
+
* some marks, such as the Devanagari virama, under punct. Derived once from the runtime's own
|
|
17627
|
+
* Unicode tables. A run may also absorb unassigned code points, which are never stored.
|
|
17628
|
+
* Private-use code points are not absorbed: the other stores treat them as separators.
|
|
17629
|
+
*/
|
|
17630
|
+
let pgWordRanges;
|
|
17631
|
+
function getPgWordRanges() {
|
|
17632
|
+
if (pgWordRanges !== void 0) return pgWordRanges;
|
|
17633
|
+
const escape = (cp) => cp > 65535 ? `\\U${cp.toString(16).padStart(8, "0")}` : `\\u${cp.toString(16).padStart(4, "0")}`;
|
|
17634
|
+
const chunks = [];
|
|
17635
|
+
for (let start = 0; start <= 1114111; start += 8192) {
|
|
17636
|
+
const codePoints = [];
|
|
17637
|
+
for (let cp = start; cp < start + 8192 && cp <= 1114111; cp++) if (cp < 55296 || cp > 57343) codePoints.push(cp);
|
|
17638
|
+
chunks.push(String.fromCodePoint(...codePoints));
|
|
17639
|
+
}
|
|
17640
|
+
const text = chunks.join("");
|
|
17641
|
+
const ranges = [];
|
|
17642
|
+
for (const match of text.matchAll(/[\p{L}\p{M}\p{N}\p{Cn}]+/gu)) {
|
|
17643
|
+
const first = match[0].codePointAt(0);
|
|
17644
|
+
const lastUnit = match.index + match[0].length - 1;
|
|
17645
|
+
const last = text.codePointAt(text.charCodeAt(lastUnit) >= 56320 && text.charCodeAt(lastUnit) <= 57343 ? lastUnit - 1 : lastUnit);
|
|
17646
|
+
ranges.push(first === last ? escape(first) : `${escape(first)}-${escape(last)}`);
|
|
17647
|
+
}
|
|
17648
|
+
pgWordRanges = ranges.join("");
|
|
17649
|
+
return pgWordRanges;
|
|
17650
|
+
}
|
|
17543
17651
|
function compileScalarPredicate(predicate, registry, parameterOffset, allowMetadata = false) {
|
|
17544
17652
|
if (predicate.type === "boolean") {
|
|
17545
17653
|
const values = [];
|
|
@@ -17583,6 +17691,13 @@ function compileScalarPredicate(predicate, registry, parameterOffset, allowMetad
|
|
|
17583
17691
|
values: fieldValues
|
|
17584
17692
|
};
|
|
17585
17693
|
}
|
|
17694
|
+
if (predicate.type === "text") {
|
|
17695
|
+
const found = `strpos(${`' ' || replace(lower(regexp_replace(normalize(${field}), '[^${getPgWordRanges()}]+', ' ', 'g')), 'ς', 'σ') || ' '`}, $${parameterOffset}) > 0`;
|
|
17696
|
+
return {
|
|
17697
|
+
sql: `COALESCE(${predicate.operator === "matches" ? found : `NOT (${found})`}, false)`,
|
|
17698
|
+
values: [...fieldValues, ` ${predicate.value} `]
|
|
17699
|
+
};
|
|
17700
|
+
}
|
|
17586
17701
|
if (predicate.type === "membership") {
|
|
17587
17702
|
const list = placeholders(predicate.values, parameterOffset);
|
|
17588
17703
|
if (predicate.operator === "in") return {
|
|
@@ -23172,12 +23287,17 @@ function workflowSnapshotStatusIndexName(schemaName) {
|
|
|
23172
23287
|
/**
|
|
23173
23288
|
* Expression index on `(workflow_name, snapshot->>'status', "createdAt" DESC)` so
|
|
23174
23289
|
* listWorkflowRuns() status filters can use an index instead of scanning every snapshot.
|
|
23290
|
+
*
|
|
23291
|
+
* `concurrently` is set by init, which may build the index on a large existing table while
|
|
23292
|
+
* other processes keep writing to it. The exported DDL stays plain because migration
|
|
23293
|
+
* tooling often wraps statements in a transaction, where CONCURRENTLY is rejected.
|
|
23175
23294
|
*/
|
|
23176
|
-
function workflowSnapshotStatusIndexSQL(indexName, schemaName) {
|
|
23177
|
-
|
|
23295
|
+
function workflowSnapshotStatusIndexSQL(indexName, schemaName, concurrently = false) {
|
|
23296
|
+
const tableName = getTableName({
|
|
23178
23297
|
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
23179
23298
|
schemaName: getSchemaName(schemaName)
|
|
23180
|
-
})
|
|
23299
|
+
});
|
|
23300
|
+
return `CREATE INDEX ${concurrently ? "CONCURRENTLY " : ""}IF NOT EXISTS "${indexName}" ON ${tableName} (workflow_name, (snapshot ->> 'status'), "createdAt" DESC)`;
|
|
23181
23301
|
}
|
|
23182
23302
|
/** Base name (before any schema prefix) of the expression index backing the threadId filter. */
|
|
23183
23303
|
const WORKFLOW_SNAPSHOT_THREAD_ID_INDEX = "mastra_workflow_snapshot_threadid_idx";
|
|
@@ -23214,12 +23334,14 @@ const WORKFLOW_SNAPSHOT_THREAD_ID_EXPR = `COALESCE(jsonb_path_query_first(snapsh
|
|
|
23214
23334
|
/**
|
|
23215
23335
|
* Expression index on the snapshot-embedded thread id so listWorkflowRuns() threadId filters
|
|
23216
23336
|
* (Agent.listSuspendedRuns) can use an index instead of detoasting every snapshot.
|
|
23337
|
+
* See {@link workflowSnapshotStatusIndexSQL} for `concurrently`.
|
|
23217
23338
|
*/
|
|
23218
|
-
function workflowSnapshotThreadIdIndexSQL(indexName, schemaName) {
|
|
23219
|
-
|
|
23339
|
+
function workflowSnapshotThreadIdIndexSQL(indexName, schemaName, concurrently = false) {
|
|
23340
|
+
const tableName = getTableName({
|
|
23220
23341
|
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
23221
23342
|
schemaName: getSchemaName(schemaName)
|
|
23222
|
-
})
|
|
23343
|
+
});
|
|
23344
|
+
return `CREATE INDEX ${concurrently ? "CONCURRENTLY " : ""}IF NOT EXISTS "${indexName}" ON ${tableName} ((${WORKFLOW_SNAPSHOT_THREAD_ID_EXPR}))`;
|
|
23223
23345
|
}
|
|
23224
23346
|
var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorage {
|
|
23225
23347
|
#db;
|
|
@@ -23316,13 +23438,13 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
23316
23438
|
if (await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "snapshot") !== "jsonb") return;
|
|
23317
23439
|
const indexName = workflowSnapshotStatusIndexName(this.#schema);
|
|
23318
23440
|
try {
|
|
23319
|
-
await this.#db.createIndexFromStatement(indexName, workflowSnapshotStatusIndexSQL(indexName, this.#schema));
|
|
23441
|
+
await this.#db.createIndexFromStatement(indexName, workflowSnapshotStatusIndexSQL(indexName, this.#schema, true));
|
|
23320
23442
|
} catch (error) {
|
|
23321
23443
|
this.logger?.warn?.(`Failed to create index ${indexName}:`, error);
|
|
23322
23444
|
}
|
|
23323
23445
|
const threadIdIndexName = workflowSnapshotThreadIdIndexName(this.#schema);
|
|
23324
23446
|
try {
|
|
23325
|
-
await this.#db.createIndexFromStatement(threadIdIndexName, workflowSnapshotThreadIdIndexSQL(threadIdIndexName, this.#schema));
|
|
23447
|
+
await this.#db.createIndexFromStatement(threadIdIndexName, workflowSnapshotThreadIdIndexSQL(threadIdIndexName, this.#schema, true));
|
|
23326
23448
|
} catch (error) {
|
|
23327
23449
|
this.logger?.warn?.(`Failed to create index ${threadIdIndexName}:`, error);
|
|
23328
23450
|
}
|