@mastra/pg 1.16.0 → 1.17.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/CHANGELOG.md +43 -6
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-memory-working-memory.md +1 -1
- package/dist/docs/references/reference-storage-retention.md +1 -1
- package/dist/docs/references/reference-tools-vector-query-tool.md +22 -0
- package/dist/index.cjs +468 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +470 -6
- package/dist/index.js.map +1 -1
- package/dist/shared/config.d.ts.map +1 -1
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +1 -1
- package/dist/storage/factory-storage.d.ts +45 -0
- package/dist/storage/factory-storage.d.ts.map +1 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/vector/index.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
2
|
-
import { BRANCH_SPAN_TYPES, createVectorErrorId, AgentsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_SCHEMAS, createStorageErrorId, normalizePerPage, calculatePagination, BackgroundTasksStorage, TABLE_BACKGROUND_TASKS, BlobStore, TABLE_SKILL_BLOBS, ChannelsStorage, TABLE_CHANNEL_INSTALLATIONS, TABLE_CHANNEL_CONFIG, DatasetsStorage, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, DATASETS_SCHEMA, TABLE_CONFIGS, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, ensureDate, safelyParseJSON, hasErrorCode, ExperimentsStorage, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, FavoritesStorage, TABLE_FAVORITES, MCPClientsStorage, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, MCPServersStorage, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, MemoryStorage, TABLE_THREADS, TABLE_RESOURCES, TABLE_MESSAGES, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, NotificationsStorage, TABLE_NOTIFICATIONS, ObservabilityStorage, TABLE_SPANS, listTracesArgsSchema, toTraceSpans, PromptBlocksStorage, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, SchedulesStorage, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, ScorerDefinitionsStorage, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, ScoresStorage, TABLE_SCORERS, SkillsStorage, TABLE_SKILLS, TABLE_SKILL_VERSIONS, ToolProviderConnectionsStorage, TABLE_TOOL_PROVIDER_CONNECTIONS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, mergeWorkflowStepResult, WorkspacesStorage, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, MastraCompositeStore, TraceStatus, getDefaultValue, parseDuration, listBranchesArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listFeedbackArgsSchema, normalizeScheduleTarget, transformScoreRow as transformScoreRow$1, getSqlType, EntityType, METRIC_DISTINCT_COLUMNS } from '@mastra/core/storage';
|
|
2
|
+
import { BRANCH_SPAN_TYPES, createVectorErrorId, AgentsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_SCHEMAS, createStorageErrorId, normalizePerPage, calculatePagination, BackgroundTasksStorage, TABLE_BACKGROUND_TASKS, BlobStore, TABLE_SKILL_BLOBS, ChannelsStorage, TABLE_CHANNEL_INSTALLATIONS, TABLE_CHANNEL_CONFIG, DatasetsStorage, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, DATASETS_SCHEMA, TABLE_CONFIGS, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, ensureDate, safelyParseJSON, hasErrorCode, ExperimentsStorage, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, FavoritesStorage, TABLE_FAVORITES, MCPClientsStorage, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, MCPServersStorage, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, MemoryStorage, TABLE_THREADS, TABLE_RESOURCES, TABLE_MESSAGES, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, NotificationsStorage, TABLE_NOTIFICATIONS, ObservabilityStorage, TABLE_SPANS, listTracesArgsSchema, toTraceSpans, PromptBlocksStorage, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, SchedulesStorage, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, ScorerDefinitionsStorage, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, ScoresStorage, TABLE_SCORERS, SkillsStorage, TABLE_SKILLS, TABLE_SKILL_VERSIONS, ToolProviderConnectionsStorage, TABLE_TOOL_PROVIDER_CONNECTIONS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, mergeWorkflowStepResult, WorkspacesStorage, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, FactoryStorage, MastraCompositeStore, TraceStatus, getDefaultValue, parseDuration, listBranchesArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listFeedbackArgsSchema, normalizeScheduleTarget, transformScoreRow as transformScoreRow$1, UniqueViolationError, getSqlType, EntityType, METRIC_DISTINCT_COLUMNS } from '@mastra/core/storage';
|
|
3
3
|
import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
|
|
4
4
|
import { MastraVector, validateTopK, validateUpsertInput } from '@mastra/core/vector';
|
|
5
5
|
import { Mutex } from 'async-mutex';
|
|
6
6
|
import * as pg from 'pg';
|
|
7
|
-
import { Pool } from 'pg';
|
|
7
|
+
import pg__default, { Pool } from 'pg';
|
|
8
8
|
import xxhash from 'xxhash-wasm';
|
|
9
9
|
import { parse } from 'pg-connection-string';
|
|
10
10
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
11
11
|
import { MastraBase } from '@mastra/core/base';
|
|
12
|
-
import { randomUUID } from 'crypto';
|
|
12
|
+
import { randomUUID, createHash } from 'crypto';
|
|
13
13
|
import { MessageList } from '@mastra/core/agent';
|
|
14
14
|
import { coreFeatures } from '@mastra/core/features';
|
|
15
15
|
import { saveScorePayloadSchema } from '@mastra/core/evals';
|
|
@@ -646,6 +646,14 @@ var PgVector = class extends MastraVector {
|
|
|
646
646
|
throw new Error("PgVector: invalid configuration provided");
|
|
647
647
|
}
|
|
648
648
|
this.pool = new pg.Pool(poolConfig);
|
|
649
|
+
this.pool.on("error", (err) => {
|
|
650
|
+
this.logger?.warn?.(
|
|
651
|
+
"PgVector: idle pool client error (pool discards the client and reconnects on next checkout)",
|
|
652
|
+
{
|
|
653
|
+
err: err instanceof Error ? err.message : err
|
|
654
|
+
}
|
|
655
|
+
);
|
|
656
|
+
});
|
|
649
657
|
this.cacheWarmupPromise = (async () => {
|
|
650
658
|
try {
|
|
651
659
|
const existingIndexes = await this.listIndexes();
|
|
@@ -2348,6 +2356,11 @@ function resolvePgConfig(config) {
|
|
|
2348
2356
|
ssl: config.ssl
|
|
2349
2357
|
});
|
|
2350
2358
|
}
|
|
2359
|
+
pool.on("error", (err) => {
|
|
2360
|
+
console.warn(
|
|
2361
|
+
`resolvePgConfig: idle pool client error (pool discards the client and reconnects on next checkout): ${err instanceof Error ? err.message : String(err)}`
|
|
2362
|
+
);
|
|
2363
|
+
});
|
|
2351
2364
|
return {
|
|
2352
2365
|
client: new PoolAdapter(pool),
|
|
2353
2366
|
schemaName: config.schemaName,
|
|
@@ -15553,8 +15566,8 @@ var ObservabilityStoragePostgresVNext = class _ObservabilityStoragePostgresVNext
|
|
|
15553
15566
|
return { preferred: "insert-only", supported: ["insert-only"] };
|
|
15554
15567
|
}
|
|
15555
15568
|
getFeatures() {
|
|
15556
|
-
if (!deltaPollingFeatureEnabled()) return
|
|
15557
|
-
return ["delta-polling"];
|
|
15569
|
+
if (!deltaPollingFeatureEnabled()) return ["metrics", "logs"];
|
|
15570
|
+
return ["metrics", "logs", "delta-polling"];
|
|
15558
15571
|
}
|
|
15559
15572
|
async #run(op, fn, details) {
|
|
15560
15573
|
try {
|
|
@@ -20160,6 +20173,435 @@ var WorkspacesPG = class _WorkspacesPG extends WorkspacesStorage {
|
|
|
20160
20173
|
};
|
|
20161
20174
|
}
|
|
20162
20175
|
};
|
|
20176
|
+
var IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
20177
|
+
var PG_UNIQUE_VIOLATION = "23505";
|
|
20178
|
+
var COLUMN_DDL = {
|
|
20179
|
+
text: "TEXT",
|
|
20180
|
+
bigint: "BIGINT",
|
|
20181
|
+
integer: "INTEGER",
|
|
20182
|
+
boolean: "BOOLEAN",
|
|
20183
|
+
json: "JSONB",
|
|
20184
|
+
timestamp: "TIMESTAMPTZ",
|
|
20185
|
+
"uuid-pk": "UUID"
|
|
20186
|
+
};
|
|
20187
|
+
function assertIdentifier(kind, name) {
|
|
20188
|
+
if (!IDENTIFIER_RE.test(name)) {
|
|
20189
|
+
throw new Error(`PgFactoryStorage: invalid ${kind} identifier '${name}'`);
|
|
20190
|
+
}
|
|
20191
|
+
}
|
|
20192
|
+
function isUniqueViolation(error) {
|
|
20193
|
+
return typeof error === "object" && error !== null && error.code === PG_UNIQUE_VIOLATION;
|
|
20194
|
+
}
|
|
20195
|
+
function primaryKeyOf(schema) {
|
|
20196
|
+
const pks = Object.entries(schema.columns).filter(([, spec]) => spec.type === "uuid-pk" || spec.primaryKey);
|
|
20197
|
+
if (pks.length !== 1) {
|
|
20198
|
+
throw new Error(
|
|
20199
|
+
`PgFactoryStorage: collection '${schema.name}' must declare exactly one primary key (found ${pks.length})`
|
|
20200
|
+
);
|
|
20201
|
+
}
|
|
20202
|
+
return pks[0][0];
|
|
20203
|
+
}
|
|
20204
|
+
function assertUpsertConflict(schema, conflictKeys, row) {
|
|
20205
|
+
const keys = new Set(conflictKeys);
|
|
20206
|
+
if (keys.size !== conflictKeys.length || conflictKeys.some((key) => row[key] === void 0)) {
|
|
20207
|
+
throw new Error(
|
|
20208
|
+
`PgFactoryStorage: upsert conflict keys for '${schema.name}' must be unique and present in the row`
|
|
20209
|
+
);
|
|
20210
|
+
}
|
|
20211
|
+
const primaryKey = primaryKeyOf(schema);
|
|
20212
|
+
const candidates = [{ columns: [primaryKey] }, ...schema.uniqueIndexes ?? []];
|
|
20213
|
+
const matches = candidates.some((candidate) => {
|
|
20214
|
+
if (candidate.columns.length !== keys.size || candidate.columns.some((column) => !keys.has(column))) return false;
|
|
20215
|
+
if ("whereNotNull" in candidate && candidate.whereNotNull && row[candidate.whereNotNull] == null) return false;
|
|
20216
|
+
if ("whereNull" in candidate && candidate.whereNull && row[candidate.whereNull] != null) return false;
|
|
20217
|
+
return true;
|
|
20218
|
+
});
|
|
20219
|
+
if (!matches) {
|
|
20220
|
+
throw new Error(
|
|
20221
|
+
`PgFactoryStorage: upsert conflict keys [${conflictKeys.join(", ")}] do not match an applicable primary key or unique index on '${schema.name}'`
|
|
20222
|
+
);
|
|
20223
|
+
}
|
|
20224
|
+
}
|
|
20225
|
+
function serializeDefault(value) {
|
|
20226
|
+
if (typeof value === "string") return `'${value.replace(/'/g, "''")}'`;
|
|
20227
|
+
return String(value);
|
|
20228
|
+
}
|
|
20229
|
+
function hashAdvisoryLockKey(key) {
|
|
20230
|
+
const digest = createHash("sha256").update(key).digest();
|
|
20231
|
+
return [digest.readInt32BE(0), digest.readInt32BE(4)];
|
|
20232
|
+
}
|
|
20233
|
+
var PgFactoryStorageOps = class {
|
|
20234
|
+
#pool;
|
|
20235
|
+
#schemas;
|
|
20236
|
+
constructor(pool, schemas) {
|
|
20237
|
+
this.#pool = pool;
|
|
20238
|
+
this.#schemas = schemas;
|
|
20239
|
+
}
|
|
20240
|
+
#schema(collection) {
|
|
20241
|
+
const schema = this.#schemas.get(collection);
|
|
20242
|
+
if (!schema) {
|
|
20243
|
+
throw new Error(
|
|
20244
|
+
`PgFactoryStorage: unknown collection '${collection}' \u2014 register it via ensureCollections() first`
|
|
20245
|
+
);
|
|
20246
|
+
}
|
|
20247
|
+
return schema;
|
|
20248
|
+
}
|
|
20249
|
+
#column(schema, column) {
|
|
20250
|
+
const spec = schema.columns[column];
|
|
20251
|
+
if (!spec) {
|
|
20252
|
+
throw new Error(`PgFactoryStorage: unknown column '${column}' on collection '${schema.name}'`);
|
|
20253
|
+
}
|
|
20254
|
+
return spec;
|
|
20255
|
+
}
|
|
20256
|
+
#serialize(spec, value) {
|
|
20257
|
+
if (value === null || value === void 0) return null;
|
|
20258
|
+
switch (spec.type) {
|
|
20259
|
+
case "timestamp":
|
|
20260
|
+
return value instanceof Date ? value : new Date(String(value));
|
|
20261
|
+
case "boolean":
|
|
20262
|
+
return Boolean(value);
|
|
20263
|
+
case "json":
|
|
20264
|
+
return JSON.stringify(value);
|
|
20265
|
+
case "bigint":
|
|
20266
|
+
case "integer":
|
|
20267
|
+
return Number(value);
|
|
20268
|
+
default:
|
|
20269
|
+
return String(value);
|
|
20270
|
+
}
|
|
20271
|
+
}
|
|
20272
|
+
#deserializeRow(schema, raw) {
|
|
20273
|
+
const row = {};
|
|
20274
|
+
for (const [name, spec] of Object.entries(schema.columns)) {
|
|
20275
|
+
const value = raw[name];
|
|
20276
|
+
if (value === null || value === void 0) {
|
|
20277
|
+
row[name] = null;
|
|
20278
|
+
continue;
|
|
20279
|
+
}
|
|
20280
|
+
switch (spec.type) {
|
|
20281
|
+
case "timestamp":
|
|
20282
|
+
row[name] = value instanceof Date ? value : new Date(String(value));
|
|
20283
|
+
break;
|
|
20284
|
+
case "boolean":
|
|
20285
|
+
row[name] = Boolean(value);
|
|
20286
|
+
break;
|
|
20287
|
+
case "json":
|
|
20288
|
+
row[name] = typeof value === "string" ? JSON.parse(value) : value;
|
|
20289
|
+
break;
|
|
20290
|
+
case "bigint":
|
|
20291
|
+
case "integer":
|
|
20292
|
+
row[name] = Number(value);
|
|
20293
|
+
break;
|
|
20294
|
+
default:
|
|
20295
|
+
row[name] = String(value);
|
|
20296
|
+
}
|
|
20297
|
+
}
|
|
20298
|
+
return row;
|
|
20299
|
+
}
|
|
20300
|
+
/** Builds `WHERE` SQL (without the keyword). `{}` yields a match-all clause. */
|
|
20301
|
+
#buildWhere(schema, where, startIndex = 1) {
|
|
20302
|
+
const clauses = [];
|
|
20303
|
+
const args = [];
|
|
20304
|
+
let index = startIndex;
|
|
20305
|
+
for (const [column, condition] of Object.entries(where)) {
|
|
20306
|
+
const spec = this.#column(schema, column);
|
|
20307
|
+
if (condition !== null && typeof condition === "object" && !(condition instanceof Date) && "in" in condition) {
|
|
20308
|
+
if (condition.in.length === 0) {
|
|
20309
|
+
clauses.push("FALSE");
|
|
20310
|
+
continue;
|
|
20311
|
+
}
|
|
20312
|
+
const nonNull = condition.in.filter((value) => value !== null);
|
|
20313
|
+
const includesNull = nonNull.length !== condition.in.length;
|
|
20314
|
+
const inClause = nonNull.length > 0 ? `"${column}" IN (${nonNull.map(() => `$${index++}`).join(", ")})` : void 0;
|
|
20315
|
+
clauses.push(
|
|
20316
|
+
inClause && includesNull ? `(${inClause} OR "${column}" IS NULL)` : inClause ?? `"${column}" IS NULL`
|
|
20317
|
+
);
|
|
20318
|
+
args.push(...nonNull.map((value) => this.#serialize(spec, value)));
|
|
20319
|
+
} else if (condition === null) {
|
|
20320
|
+
clauses.push(`"${column}" IS NULL`);
|
|
20321
|
+
} else {
|
|
20322
|
+
clauses.push(`"${column}" = $${index++}`);
|
|
20323
|
+
args.push(this.#serialize(spec, condition));
|
|
20324
|
+
}
|
|
20325
|
+
}
|
|
20326
|
+
return { sql: clauses.length > 0 ? clauses.join(" AND ") : "TRUE", args, nextIndex: index };
|
|
20327
|
+
}
|
|
20328
|
+
/** Keyset condition: rows strictly after `cursor.values` in the `orderBy` order. */
|
|
20329
|
+
#buildCursor(schema, orderBy, values, startIndex) {
|
|
20330
|
+
if (values.length !== orderBy.length) {
|
|
20331
|
+
throw new Error(`PgFactoryStorage: cursor has ${values.length} values but orderBy has ${orderBy.length} columns`);
|
|
20332
|
+
}
|
|
20333
|
+
const branches = [];
|
|
20334
|
+
const args = [];
|
|
20335
|
+
let index = startIndex;
|
|
20336
|
+
for (let i = 0; i < orderBy.length; i++) {
|
|
20337
|
+
const parts = [];
|
|
20338
|
+
for (let j = 0; j < i; j++) {
|
|
20339
|
+
const [column2] = orderBy[j];
|
|
20340
|
+
parts.push(`"${column2}" = $${index++}`);
|
|
20341
|
+
args.push(this.#serialize(this.#column(schema, column2), values[j]));
|
|
20342
|
+
}
|
|
20343
|
+
const [column, dir] = orderBy[i];
|
|
20344
|
+
parts.push(`"${column}" ${dir === "desc" ? "<" : ">"} $${index++}`);
|
|
20345
|
+
args.push(this.#serialize(this.#column(schema, column), values[i]));
|
|
20346
|
+
branches.push(`(${parts.join(" AND ")})`);
|
|
20347
|
+
}
|
|
20348
|
+
return { sql: `(${branches.join(" OR ")})`, args, nextIndex: index };
|
|
20349
|
+
}
|
|
20350
|
+
#buildSelect(schema, where, opts, forUpdate = false) {
|
|
20351
|
+
const filter = this.#buildWhere(schema, where);
|
|
20352
|
+
let sql = `SELECT * FROM "${schema.name}" WHERE ${filter.sql}`;
|
|
20353
|
+
const args = [...filter.args];
|
|
20354
|
+
let index = filter.nextIndex;
|
|
20355
|
+
if (opts?.cursor) {
|
|
20356
|
+
if (!opts.orderBy || opts.orderBy.length === 0) {
|
|
20357
|
+
throw new Error("PgFactoryStorage: cursor pagination requires orderBy");
|
|
20358
|
+
}
|
|
20359
|
+
const cursor = this.#buildCursor(schema, opts.orderBy, opts.cursor.values, index);
|
|
20360
|
+
sql += ` AND ${cursor.sql}`;
|
|
20361
|
+
args.push(...cursor.args);
|
|
20362
|
+
index = cursor.nextIndex;
|
|
20363
|
+
}
|
|
20364
|
+
if (opts?.orderBy && opts.orderBy.length > 0) {
|
|
20365
|
+
const order = opts.orderBy.map(([column, dir]) => {
|
|
20366
|
+
this.#column(schema, column);
|
|
20367
|
+
return `"${column}" ${dir === "desc" ? "DESC" : "ASC"}`;
|
|
20368
|
+
}).join(", ");
|
|
20369
|
+
sql += ` ORDER BY ${order}`;
|
|
20370
|
+
}
|
|
20371
|
+
if (opts?.limit !== void 0) {
|
|
20372
|
+
sql += ` LIMIT $${index++}`;
|
|
20373
|
+
args.push(opts.limit);
|
|
20374
|
+
}
|
|
20375
|
+
if (forUpdate) sql += " FOR UPDATE";
|
|
20376
|
+
return { sql, args };
|
|
20377
|
+
}
|
|
20378
|
+
async #select(queryable, collection, where, opts, forUpdate = false) {
|
|
20379
|
+
const schema = this.#schema(collection);
|
|
20380
|
+
const { sql, args } = this.#buildSelect(schema, where, opts, forUpdate);
|
|
20381
|
+
const result = await queryable.query(sql, args);
|
|
20382
|
+
return result.rows.map((raw) => this.#deserializeRow(schema, raw));
|
|
20383
|
+
}
|
|
20384
|
+
async findOne(collection, where) {
|
|
20385
|
+
const rows = await this.#select(this.#pool, collection, where, { limit: 1 });
|
|
20386
|
+
return rows[0] ?? null;
|
|
20387
|
+
}
|
|
20388
|
+
async findMany(collection, where, opts) {
|
|
20389
|
+
return this.#select(this.#pool, collection, where, opts);
|
|
20390
|
+
}
|
|
20391
|
+
async insertOne(collection, row) {
|
|
20392
|
+
const schema = this.#schema(collection);
|
|
20393
|
+
const pk = primaryKeyOf(schema);
|
|
20394
|
+
const values = { ...row };
|
|
20395
|
+
if (schema.columns[pk].type === "uuid-pk" && values[pk] === void 0) {
|
|
20396
|
+
values[pk] = randomUUID();
|
|
20397
|
+
}
|
|
20398
|
+
const columns = Object.keys(values).filter((column) => values[column] !== void 0);
|
|
20399
|
+
for (const column of columns) this.#column(schema, column);
|
|
20400
|
+
const sql = `INSERT INTO "${schema.name}" (${columns.map((c) => `"${c}"`).join(", ")}) VALUES (${columns.map((_, i) => `$${i + 1}`).join(", ")}) RETURNING *`;
|
|
20401
|
+
const args = columns.map((column) => this.#serialize(this.#column(schema, column), values[column]));
|
|
20402
|
+
try {
|
|
20403
|
+
const result = await this.#pool.query(sql, args);
|
|
20404
|
+
return this.#deserializeRow(schema, result.rows[0]);
|
|
20405
|
+
} catch (error) {
|
|
20406
|
+
if (isUniqueViolation(error)) throw new UniqueViolationError(collection, { cause: error });
|
|
20407
|
+
throw error;
|
|
20408
|
+
}
|
|
20409
|
+
}
|
|
20410
|
+
async upsertOne(collection, conflictKeys, row) {
|
|
20411
|
+
const schema = this.#schema(collection);
|
|
20412
|
+
const pk = primaryKeyOf(schema);
|
|
20413
|
+
assertUpsertConflict(schema, conflictKeys, row);
|
|
20414
|
+
const keyWhere = Object.fromEntries(conflictKeys.map((key) => [key, row[key]]));
|
|
20415
|
+
let lastError;
|
|
20416
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
20417
|
+
const existing = await this.findOne(collection, keyWhere);
|
|
20418
|
+
if (existing) {
|
|
20419
|
+
const set = Object.fromEntries(
|
|
20420
|
+
Object.entries(row).filter(
|
|
20421
|
+
([column, value]) => value !== void 0 && column !== pk && !conflictKeys.includes(column)
|
|
20422
|
+
)
|
|
20423
|
+
);
|
|
20424
|
+
if (Object.keys(set).length > 0) {
|
|
20425
|
+
await this.updateMany(collection, { [pk]: existing[pk] }, set);
|
|
20426
|
+
}
|
|
20427
|
+
const updated = await this.findOne(collection, { [pk]: existing[pk] });
|
|
20428
|
+
if (!updated) continue;
|
|
20429
|
+
return updated;
|
|
20430
|
+
}
|
|
20431
|
+
try {
|
|
20432
|
+
return await this.insertOne(collection, row);
|
|
20433
|
+
} catch (error) {
|
|
20434
|
+
if (!(error instanceof UniqueViolationError)) throw error;
|
|
20435
|
+
lastError = error;
|
|
20436
|
+
}
|
|
20437
|
+
}
|
|
20438
|
+
throw lastError ?? new Error(`PgFactoryStorage: upsert into '${collection}' did not converge`);
|
|
20439
|
+
}
|
|
20440
|
+
async updateMany(collection, where, set) {
|
|
20441
|
+
return this.#updateMany(this.#pool, collection, where, set);
|
|
20442
|
+
}
|
|
20443
|
+
async #updateMany(queryable, collection, where, set) {
|
|
20444
|
+
const schema = this.#schema(collection);
|
|
20445
|
+
const columns = Object.keys(set).filter((column) => set[column] !== void 0);
|
|
20446
|
+
if (columns.length === 0) return 0;
|
|
20447
|
+
const assignments = columns.map((column, i) => `"${column}" = $${i + 1}`).join(", ");
|
|
20448
|
+
const filter = this.#buildWhere(schema, where, columns.length + 1);
|
|
20449
|
+
const args = [...columns.map((column) => this.#serialize(this.#column(schema, column), set[column])), ...filter.args];
|
|
20450
|
+
const result = await queryable.query(`UPDATE "${schema.name}" SET ${assignments} WHERE ${filter.sql}`, args);
|
|
20451
|
+
return result.rowCount ?? 0;
|
|
20452
|
+
}
|
|
20453
|
+
async deleteMany(collection, where) {
|
|
20454
|
+
const schema = this.#schema(collection);
|
|
20455
|
+
const filter = this.#buildWhere(schema, where);
|
|
20456
|
+
const result = await this.#pool.query(`DELETE FROM "${schema.name}" WHERE ${filter.sql}`, filter.args);
|
|
20457
|
+
return result.rowCount ?? 0;
|
|
20458
|
+
}
|
|
20459
|
+
async updateAtomic(collection, where, fn) {
|
|
20460
|
+
const schema = this.#schema(collection);
|
|
20461
|
+
const pk = primaryKeyOf(schema);
|
|
20462
|
+
const client = await this.#pool.connect();
|
|
20463
|
+
try {
|
|
20464
|
+
await client.query("BEGIN");
|
|
20465
|
+
try {
|
|
20466
|
+
const rows = await this.#select(client, collection, where, { limit: 1 }, true);
|
|
20467
|
+
const row = rows[0];
|
|
20468
|
+
if (!row) {
|
|
20469
|
+
await client.query("COMMIT");
|
|
20470
|
+
return null;
|
|
20471
|
+
}
|
|
20472
|
+
const patch = await fn(row);
|
|
20473
|
+
let result = row;
|
|
20474
|
+
if (patch !== null) {
|
|
20475
|
+
const pkWhere = { [pk]: row[pk] };
|
|
20476
|
+
await this.#updateMany(client, collection, pkWhere, patch);
|
|
20477
|
+
const updated = await this.#select(client, collection, pkWhere, { limit: 1 });
|
|
20478
|
+
result = updated[0] ?? null;
|
|
20479
|
+
}
|
|
20480
|
+
await client.query("COMMIT");
|
|
20481
|
+
return result;
|
|
20482
|
+
} catch (error) {
|
|
20483
|
+
await client.query("ROLLBACK");
|
|
20484
|
+
throw error;
|
|
20485
|
+
}
|
|
20486
|
+
} finally {
|
|
20487
|
+
client.release();
|
|
20488
|
+
}
|
|
20489
|
+
}
|
|
20490
|
+
};
|
|
20491
|
+
var PgFactoryStorage = class extends FactoryStorage {
|
|
20492
|
+
ops;
|
|
20493
|
+
#pool;
|
|
20494
|
+
#ownsPool;
|
|
20495
|
+
#config;
|
|
20496
|
+
#schemas = /* @__PURE__ */ new Map();
|
|
20497
|
+
#mastraStorage;
|
|
20498
|
+
constructor(config) {
|
|
20499
|
+
super();
|
|
20500
|
+
this.#config = config;
|
|
20501
|
+
if ("store" in config) {
|
|
20502
|
+
this.#pool = config.store.pool;
|
|
20503
|
+
this.#ownsPool = false;
|
|
20504
|
+
this.#mastraStorage = config.store;
|
|
20505
|
+
} else {
|
|
20506
|
+
this.#pool = new pg__default.Pool({ connectionString: config.connectionString });
|
|
20507
|
+
this.#ownsPool = true;
|
|
20508
|
+
}
|
|
20509
|
+
this.ops = new PgFactoryStorageOps(this.#pool, this.#schemas);
|
|
20510
|
+
}
|
|
20511
|
+
getMastraStorage() {
|
|
20512
|
+
if (!this.#mastraStorage) {
|
|
20513
|
+
const config = this.#config;
|
|
20514
|
+
this.#mastraStorage = new PostgresStore({
|
|
20515
|
+
id: config.id ?? "pg-factory",
|
|
20516
|
+
pool: this.#pool,
|
|
20517
|
+
...config.retention ? { retention: config.retention } : {}
|
|
20518
|
+
});
|
|
20519
|
+
}
|
|
20520
|
+
return this.#mastraStorage;
|
|
20521
|
+
}
|
|
20522
|
+
async initStorage() {
|
|
20523
|
+
await this.#pool.query("SELECT 1");
|
|
20524
|
+
}
|
|
20525
|
+
async ensureCollections(schemas) {
|
|
20526
|
+
for (const schema of schemas) {
|
|
20527
|
+
await this.#ensureCollection(schema);
|
|
20528
|
+
this.#schemas.set(schema.name, schema);
|
|
20529
|
+
}
|
|
20530
|
+
}
|
|
20531
|
+
async close() {
|
|
20532
|
+
if (this.#ownsPool) {
|
|
20533
|
+
await this.#pool.end();
|
|
20534
|
+
} else {
|
|
20535
|
+
await this.#mastraStorage?.close();
|
|
20536
|
+
}
|
|
20537
|
+
}
|
|
20538
|
+
authDatabase() {
|
|
20539
|
+
return { dialect: "postgres", pool: this.#pool };
|
|
20540
|
+
}
|
|
20541
|
+
/**
|
|
20542
|
+
* Run `fn` while holding a Postgres transaction-scoped advisory lock for
|
|
20543
|
+
* `key`, serializing callers across replicas. The lock releases when the
|
|
20544
|
+
* transaction ends (commit, rollback, or connection loss), so a crashed
|
|
20545
|
+
* replica can never hold it forever.
|
|
20546
|
+
*/
|
|
20547
|
+
async withDistributedLock(key, fn) {
|
|
20548
|
+
const [k1, k2] = hashAdvisoryLockKey(key);
|
|
20549
|
+
const client = await this.#pool.connect();
|
|
20550
|
+
try {
|
|
20551
|
+
await client.query("BEGIN");
|
|
20552
|
+
await client.query("SELECT pg_advisory_xact_lock($1, $2)", [k1, k2]);
|
|
20553
|
+
try {
|
|
20554
|
+
const result = await fn();
|
|
20555
|
+
await client.query("COMMIT");
|
|
20556
|
+
return result;
|
|
20557
|
+
} catch (error) {
|
|
20558
|
+
await client.query("ROLLBACK");
|
|
20559
|
+
throw error;
|
|
20560
|
+
}
|
|
20561
|
+
} finally {
|
|
20562
|
+
client.release();
|
|
20563
|
+
}
|
|
20564
|
+
}
|
|
20565
|
+
#columnDdl(name, spec) {
|
|
20566
|
+
assertIdentifier("column", name);
|
|
20567
|
+
let ddl = `"${name}" ${COLUMN_DDL[spec.type]}`;
|
|
20568
|
+
if (spec.type === "uuid-pk" || spec.primaryKey) ddl += " PRIMARY KEY";
|
|
20569
|
+
if (!spec.nullable && spec.type !== "uuid-pk" && !spec.primaryKey) ddl += " NOT NULL";
|
|
20570
|
+
if (spec.default !== void 0) ddl += ` DEFAULT ${serializeDefault(spec.default)}`;
|
|
20571
|
+
return ddl;
|
|
20572
|
+
}
|
|
20573
|
+
async #ensureCollection(schema) {
|
|
20574
|
+
assertIdentifier("collection", schema.name);
|
|
20575
|
+
primaryKeyOf(schema);
|
|
20576
|
+
const columns = Object.entries(schema.columns).map(([name, spec]) => this.#columnDdl(name, spec));
|
|
20577
|
+
await this.#pool.query(`CREATE TABLE IF NOT EXISTS "${schema.name}" (${columns.join(", ")})`);
|
|
20578
|
+
for (const [name, spec] of Object.entries(schema.columns)) {
|
|
20579
|
+
await this.#pool.query(`ALTER TABLE "${schema.name}" ADD COLUMN IF NOT EXISTS ${this.#columnDdl(name, spec)}`);
|
|
20580
|
+
}
|
|
20581
|
+
for (const index of schema.uniqueIndexes ?? []) {
|
|
20582
|
+
assertIdentifier("index", index.name);
|
|
20583
|
+
index.columns.forEach((column) => assertIdentifier("column", column));
|
|
20584
|
+
let where = "";
|
|
20585
|
+
if (index.whereNotNull) {
|
|
20586
|
+
assertIdentifier("column", index.whereNotNull);
|
|
20587
|
+
where = ` WHERE "${index.whereNotNull}" IS NOT NULL`;
|
|
20588
|
+
} else if (index.whereNull) {
|
|
20589
|
+
assertIdentifier("column", index.whereNull);
|
|
20590
|
+
where = ` WHERE "${index.whereNull}" IS NULL`;
|
|
20591
|
+
}
|
|
20592
|
+
await this.#pool.query(
|
|
20593
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS "${index.name}" ON "${schema.name}" (${index.columns.map((c) => `"${c}"`).join(", ")})${where}`
|
|
20594
|
+
);
|
|
20595
|
+
}
|
|
20596
|
+
for (const index of schema.indexes ?? []) {
|
|
20597
|
+
assertIdentifier("index", index.name);
|
|
20598
|
+
index.columns.forEach((column) => assertIdentifier("column", column));
|
|
20599
|
+
await this.#pool.query(
|
|
20600
|
+
`CREATE INDEX IF NOT EXISTS "${index.name}" ON "${schema.name}" (${index.columns.map((c) => `"${c}"`).join(", ")})`
|
|
20601
|
+
);
|
|
20602
|
+
}
|
|
20603
|
+
}
|
|
20604
|
+
};
|
|
20163
20605
|
|
|
20164
20606
|
// src/storage/index.ts
|
|
20165
20607
|
var DEFAULT_MAX_CONNECTIONS = 20;
|
|
@@ -20284,6 +20726,18 @@ var PostgresStore = class extends MastraCompositeStore {
|
|
|
20284
20726
|
}
|
|
20285
20727
|
}
|
|
20286
20728
|
createPool(config) {
|
|
20729
|
+
const pool = this.#buildPool(config);
|
|
20730
|
+
pool.on("error", (err) => {
|
|
20731
|
+
this.logger?.warn?.(
|
|
20732
|
+
"PostgresStore: idle pool client error (pool discards the client and reconnects on next checkout)",
|
|
20733
|
+
{
|
|
20734
|
+
err: err instanceof Error ? err.message : err
|
|
20735
|
+
}
|
|
20736
|
+
);
|
|
20737
|
+
});
|
|
20738
|
+
return pool;
|
|
20739
|
+
}
|
|
20740
|
+
#buildPool(config) {
|
|
20287
20741
|
if (isConnectionStringConfig(config)) {
|
|
20288
20742
|
return createConnectionStringPool(config);
|
|
20289
20743
|
}
|
|
@@ -20391,6 +20845,16 @@ var PostgresStoreVNext = class extends PostgresStore {
|
|
|
20391
20845
|
const observabilityClient = built.client;
|
|
20392
20846
|
this.#observabilityPool = built.pool;
|
|
20393
20847
|
this.#ownsObservabilityPool = built.owned;
|
|
20848
|
+
if (built.owned) {
|
|
20849
|
+
built.pool.on("error", (err) => {
|
|
20850
|
+
this.logger?.warn?.(
|
|
20851
|
+
"PostgresStoreVNext: idle observability pool client error (pool discards the client and reconnects on next checkout)",
|
|
20852
|
+
{
|
|
20853
|
+
err: err instanceof Error ? err.message : err
|
|
20854
|
+
}
|
|
20855
|
+
);
|
|
20856
|
+
});
|
|
20857
|
+
}
|
|
20394
20858
|
const observability = new ObservabilityStoragePostgresVNext({
|
|
20395
20859
|
client: observabilityClient,
|
|
20396
20860
|
schemaName: obsConfig.schemaName ?? config.schemaName,
|
|
@@ -20529,6 +20993,6 @@ Example Complex Query:
|
|
|
20529
20993
|
]
|
|
20530
20994
|
}`;
|
|
20531
20995
|
|
|
20532
|
-
export { AgentsPG, BackgroundTasksPG, BlobsPG, ChannelsPG, DatasetsPG, ExperimentsPG, FavoritesPG, MCPClientsPG, MCPServersPG, MemoryPG, NotificationsPG, ObservabilityPG, ObservabilityStoragePostgresVNext, PGVECTOR_PROMPT, PgVector, PoolAdapter, PostgresStore, PostgresStoreVNext, PromptBlocksPG, SchedulesPG, ScorerDefinitionsPG, ScoresPG, SkillsPG, ToolProviderConnectionsPG, WorkflowsPG, WorkspacesPG, exportSchemas };
|
|
20996
|
+
export { AgentsPG, BackgroundTasksPG, BlobsPG, ChannelsPG, DatasetsPG, ExperimentsPG, FavoritesPG, MCPClientsPG, MCPServersPG, MemoryPG, NotificationsPG, ObservabilityPG, ObservabilityStoragePostgresVNext, PGVECTOR_PROMPT, PgFactoryStorage, PgVector, PoolAdapter, PostgresStore, PostgresStoreVNext, PromptBlocksPG, SchedulesPG, ScorerDefinitionsPG, ScoresPG, SkillsPG, ToolProviderConnectionsPG, WorkflowsPG, WorkspacesPG, exportSchemas, hashAdvisoryLockKey };
|
|
20533
20997
|
//# sourceMappingURL=index.js.map
|
|
20534
20998
|
//# sourceMappingURL=index.js.map
|