@mastra/mssql 0.0.0-iterate-traces-ui-again-20250912091900 → 0.0.0-jail-fs-20260105160110
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 +1419 -3
- package/README.md +324 -37
- package/dist/docs/README.md +31 -0
- package/dist/docs/SKILL.md +32 -0
- package/dist/docs/SOURCE_MAP.json +6 -0
- package/dist/docs/storage/01-reference.md +141 -0
- package/dist/index.cjs +2500 -1146
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2497 -1147
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts +172 -0
- package/dist/storage/db/index.d.ts.map +1 -0
- package/dist/storage/db/utils.d.ts +21 -0
- package/dist/storage/db/utils.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +38 -52
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts +46 -0
- package/dist/storage/domains/observability/index.d.ts.map +1 -0
- package/dist/storage/domains/scores/index.d.ts +38 -25
- package/dist/storage/domains/scores/index.d.ts.map +1 -1
- package/dist/storage/domains/utils.d.ts +19 -0
- package/dist/storage/domains/utils.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +39 -28
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +115 -213
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +15 -10
- package/dist/storage/domains/legacy-evals/index.d.ts +0 -20
- package/dist/storage/domains/legacy-evals/index.d.ts.map +0 -1
- package/dist/storage/domains/operations/index.d.ts +0 -51
- package/dist/storage/domains/operations/index.d.ts.map +0 -1
- package/dist/storage/domains/traces/index.d.ts +0 -37
- package/dist/storage/domains/traces/index.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
|
|
2
|
+
import { MemoryStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES, TABLE_SCHEMAS, createStorageErrorId, normalizePerPage, calculatePagination, ObservabilityStorage, TABLE_SPANS, SPAN_SCHEMA, listTracesArgsSchema, ScoresStorage, TABLE_SCORERS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, MastraStorage, TraceStatus, getDefaultValue, transformScoreRow as transformScoreRow$1 } from '@mastra/core/storage';
|
|
3
|
+
import sql from 'mssql';
|
|
5
4
|
import { MessageList } from '@mastra/core/agent';
|
|
5
|
+
import { MastraBase } from '@mastra/core/base';
|
|
6
|
+
import { parseSqlIdentifier } from '@mastra/core/utils';
|
|
7
|
+
import { randomUUID } from 'crypto';
|
|
8
|
+
import { saveScorePayloadSchema } from '@mastra/core/evals';
|
|
6
9
|
|
|
7
10
|
// src/storage/index.ts
|
|
8
11
|
function getSchemaName(schema) {
|
|
@@ -15,157 +18,1119 @@ function getTableName({ indexName, schemaName }) {
|
|
|
15
18
|
return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
// src/storage/
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
// src/storage/db/index.ts
|
|
22
|
+
function resolveMssqlConfig(config) {
|
|
23
|
+
if ("pool" in config && !("server" in config)) {
|
|
24
|
+
return {
|
|
25
|
+
pool: config.pool,
|
|
26
|
+
schemaName: config.schemaName,
|
|
27
|
+
skipDefaultIndexes: config.skipDefaultIndexes,
|
|
28
|
+
indexes: config.indexes,
|
|
29
|
+
needsConnect: false
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const restConfig = config;
|
|
33
|
+
const pool = new sql.ConnectionPool({
|
|
34
|
+
server: restConfig.server,
|
|
35
|
+
database: restConfig.database,
|
|
36
|
+
user: restConfig.user,
|
|
37
|
+
password: restConfig.password,
|
|
38
|
+
port: restConfig.port,
|
|
39
|
+
options: restConfig.options || { encrypt: true, trustServerCertificate: true }
|
|
40
|
+
});
|
|
41
|
+
return {
|
|
42
|
+
pool,
|
|
43
|
+
schemaName: restConfig.schemaName,
|
|
44
|
+
skipDefaultIndexes: restConfig.skipDefaultIndexes,
|
|
45
|
+
indexes: restConfig.indexes,
|
|
46
|
+
needsConnect: true
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
var MssqlDB = class extends MastraBase {
|
|
50
|
+
pool;
|
|
51
|
+
schemaName;
|
|
52
|
+
skipDefaultIndexes;
|
|
53
|
+
setupSchemaPromise = null;
|
|
54
|
+
schemaSetupComplete = void 0;
|
|
55
|
+
/**
|
|
56
|
+
* Columns that participate in composite indexes need smaller sizes (NVARCHAR(100)).
|
|
57
|
+
* MSSQL has a 900-byte index key limit, so composite indexes with NVARCHAR(400) columns fail.
|
|
58
|
+
* These are typically ID/type fields that don't need 400 chars.
|
|
59
|
+
*/
|
|
60
|
+
COMPOSITE_INDEX_COLUMNS = [
|
|
61
|
+
"traceId",
|
|
62
|
+
// Used in: PRIMARY KEY (traceId, spanId), index (traceId, spanId, seq_id)
|
|
63
|
+
"spanId",
|
|
64
|
+
// Used in: PRIMARY KEY (traceId, spanId), index (traceId, spanId, seq_id)
|
|
65
|
+
"parentSpanId",
|
|
66
|
+
// Used in: index (parentSpanId, startedAt)
|
|
67
|
+
"entityType",
|
|
68
|
+
// Used in: (entityType, entityId), (entityType, entityName)
|
|
69
|
+
"entityId",
|
|
70
|
+
// Used in: (entityType, entityId)
|
|
71
|
+
"entityName",
|
|
72
|
+
// Used in: (entityType, entityName)
|
|
73
|
+
"organizationId",
|
|
74
|
+
// Used in: (organizationId, userId)
|
|
75
|
+
"userId"
|
|
76
|
+
// Used in: (organizationId, userId)
|
|
77
|
+
];
|
|
78
|
+
/**
|
|
79
|
+
* Columns that store large amounts of data and should use NVARCHAR(MAX).
|
|
80
|
+
* Avoid listing columns that participate in indexes (resourceId, thread_id, agent_name, name, etc.)
|
|
81
|
+
*/
|
|
82
|
+
LARGE_DATA_COLUMNS = [
|
|
83
|
+
"workingMemory",
|
|
84
|
+
"snapshot",
|
|
85
|
+
"metadata",
|
|
86
|
+
"content",
|
|
87
|
+
// messages.content - can be very long conversation content
|
|
88
|
+
"input",
|
|
89
|
+
// evals.input - test input data
|
|
90
|
+
"output",
|
|
91
|
+
// evals.output - test output data
|
|
92
|
+
"instructions",
|
|
93
|
+
// evals.instructions - evaluation instructions
|
|
94
|
+
"other"
|
|
95
|
+
// traces.other - additional trace data
|
|
96
|
+
];
|
|
97
|
+
getSqlType(type, isPrimaryKey = false, useLargeStorage = false, useSmallStorage = false) {
|
|
98
|
+
switch (type) {
|
|
99
|
+
case "text":
|
|
100
|
+
if (useLargeStorage) {
|
|
101
|
+
return "NVARCHAR(MAX)";
|
|
102
|
+
}
|
|
103
|
+
if (useSmallStorage) {
|
|
104
|
+
return "NVARCHAR(100)";
|
|
105
|
+
}
|
|
106
|
+
return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(400)";
|
|
107
|
+
case "timestamp":
|
|
108
|
+
return "DATETIME2(7)";
|
|
109
|
+
case "uuid":
|
|
110
|
+
return "UNIQUEIDENTIFIER";
|
|
111
|
+
case "jsonb":
|
|
112
|
+
return "NVARCHAR(MAX)";
|
|
113
|
+
case "integer":
|
|
114
|
+
return "INT";
|
|
115
|
+
case "bigint":
|
|
116
|
+
return "BIGINT";
|
|
117
|
+
case "float":
|
|
118
|
+
return "FLOAT";
|
|
119
|
+
case "boolean":
|
|
120
|
+
return "BIT";
|
|
121
|
+
default:
|
|
122
|
+
throw new MastraError({
|
|
123
|
+
id: createStorageErrorId("MSSQL", "TYPE", "NOT_SUPPORTED"),
|
|
124
|
+
domain: ErrorDomain.STORAGE,
|
|
125
|
+
category: ErrorCategory.THIRD_PARTY
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
constructor({
|
|
130
|
+
pool,
|
|
131
|
+
schemaName,
|
|
132
|
+
skipDefaultIndexes
|
|
133
|
+
}) {
|
|
134
|
+
super({ component: "STORAGE", name: "MssqlDB" });
|
|
135
|
+
this.pool = pool;
|
|
136
|
+
this.schemaName = schemaName;
|
|
137
|
+
this.skipDefaultIndexes = skipDefaultIndexes;
|
|
138
|
+
}
|
|
139
|
+
async hasColumn(table, column) {
|
|
140
|
+
const schema = this.schemaName || "dbo";
|
|
141
|
+
const request = this.pool.request();
|
|
142
|
+
request.input("schema", schema);
|
|
143
|
+
request.input("table", table);
|
|
144
|
+
request.input("column", column);
|
|
145
|
+
request.input("columnLower", column.toLowerCase());
|
|
146
|
+
const result = await request.query(
|
|
147
|
+
`SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND (COLUMN_NAME = @column OR COLUMN_NAME = @columnLower)`
|
|
148
|
+
);
|
|
149
|
+
return result.recordset.length > 0;
|
|
150
|
+
}
|
|
151
|
+
async setupSchema() {
|
|
152
|
+
if (!this.schemaName || this.schemaSetupComplete) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (!this.setupSchemaPromise) {
|
|
156
|
+
this.setupSchemaPromise = (async () => {
|
|
157
|
+
try {
|
|
158
|
+
const checkRequest = this.pool.request();
|
|
159
|
+
checkRequest.input("schemaName", this.schemaName);
|
|
160
|
+
const checkResult = await checkRequest.query(`
|
|
161
|
+
SELECT 1 AS found FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @schemaName
|
|
162
|
+
`);
|
|
163
|
+
const schemaExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
164
|
+
if (!schemaExists) {
|
|
165
|
+
try {
|
|
166
|
+
await this.pool.request().query(`CREATE SCHEMA [${this.schemaName}]`);
|
|
167
|
+
this.logger?.info?.(`Schema "${this.schemaName}" created successfully`);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
this.logger?.error?.(`Failed to create schema "${this.schemaName}"`, { error });
|
|
170
|
+
throw new Error(
|
|
171
|
+
`Unable to create schema "${this.schemaName}". This requires CREATE privilege on the database. Either create the schema manually or grant CREATE privilege to the user.`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
this.schemaSetupComplete = true;
|
|
176
|
+
this.logger?.debug?.(`Schema "${this.schemaName}" is ready for use`);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
this.schemaSetupComplete = void 0;
|
|
179
|
+
this.setupSchemaPromise = null;
|
|
180
|
+
throw error;
|
|
181
|
+
} finally {
|
|
182
|
+
this.setupSchemaPromise = null;
|
|
183
|
+
}
|
|
184
|
+
})();
|
|
185
|
+
}
|
|
186
|
+
await this.setupSchemaPromise;
|
|
187
|
+
}
|
|
188
|
+
async insert({
|
|
189
|
+
tableName,
|
|
190
|
+
record,
|
|
191
|
+
transaction
|
|
192
|
+
}) {
|
|
22
193
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
194
|
+
const columns = Object.keys(record);
|
|
195
|
+
const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
|
|
196
|
+
const paramNames = columns.map((_, i) => `@param${i}`);
|
|
197
|
+
const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${parsedColumns.map((c) => `[${c}]`).join(", ")}) VALUES (${paramNames.join(", ")})`;
|
|
198
|
+
const request = transaction ? transaction.request() : this.pool.request();
|
|
199
|
+
columns.forEach((col, i) => {
|
|
200
|
+
const value = record[col];
|
|
201
|
+
const preparedValue = this.prepareValue(value, col, tableName);
|
|
202
|
+
if (preparedValue instanceof Date) {
|
|
203
|
+
request.input(`param${i}`, sql.DateTime2, preparedValue);
|
|
204
|
+
} else if (preparedValue === null || preparedValue === void 0) {
|
|
205
|
+
request.input(`param${i}`, this.getMssqlType(tableName, col), null);
|
|
206
|
+
} else {
|
|
207
|
+
request.input(`param${i}`, preparedValue);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
await request.query(insertSql);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
throw new MastraError(
|
|
213
|
+
{
|
|
214
|
+
id: createStorageErrorId("MSSQL", "INSERT", "FAILED"),
|
|
215
|
+
domain: ErrorDomain.STORAGE,
|
|
216
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
217
|
+
details: {
|
|
218
|
+
tableName
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
error
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async clearTable({ tableName }) {
|
|
226
|
+
const fullTableName = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
|
|
227
|
+
try {
|
|
228
|
+
try {
|
|
229
|
+
await this.pool.request().query(`TRUNCATE TABLE ${fullTableName}`);
|
|
230
|
+
} catch (truncateError) {
|
|
231
|
+
if (truncateError?.number === 4712) {
|
|
232
|
+
await this.pool.request().query(`DELETE FROM ${fullTableName}`);
|
|
233
|
+
} else {
|
|
234
|
+
throw truncateError;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
} catch (error) {
|
|
238
|
+
throw new MastraError(
|
|
239
|
+
{
|
|
240
|
+
id: createStorageErrorId("MSSQL", "CLEAR_TABLE", "FAILED"),
|
|
241
|
+
domain: ErrorDomain.STORAGE,
|
|
242
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
243
|
+
details: {
|
|
244
|
+
tableName
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
error
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
getDefaultValue(type) {
|
|
252
|
+
switch (type) {
|
|
253
|
+
case "timestamp":
|
|
254
|
+
return "DEFAULT SYSUTCDATETIME()";
|
|
255
|
+
case "jsonb":
|
|
256
|
+
return "DEFAULT N'{}'";
|
|
257
|
+
case "boolean":
|
|
258
|
+
return "DEFAULT 0";
|
|
259
|
+
default:
|
|
260
|
+
return getDefaultValue(type);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async createTable({
|
|
264
|
+
tableName,
|
|
265
|
+
schema
|
|
266
|
+
}) {
|
|
267
|
+
try {
|
|
268
|
+
const uniqueConstraintColumns = tableName === TABLE_WORKFLOW_SNAPSHOT ? ["workflow_name", "run_id"] : [];
|
|
269
|
+
const columns = Object.entries(schema).map(([name, def]) => {
|
|
270
|
+
const parsedName = parseSqlIdentifier(name, "column name");
|
|
271
|
+
const constraints = [];
|
|
272
|
+
if (def.primaryKey) constraints.push("PRIMARY KEY");
|
|
273
|
+
if (!def.nullable) constraints.push("NOT NULL");
|
|
274
|
+
const isIndexed = !!def.primaryKey || uniqueConstraintColumns.includes(name);
|
|
275
|
+
const useLargeStorage = this.LARGE_DATA_COLUMNS.includes(name);
|
|
276
|
+
const useSmallStorage = this.COMPOSITE_INDEX_COLUMNS.includes(name);
|
|
277
|
+
return `[${parsedName}] ${this.getSqlType(def.type, isIndexed, useLargeStorage, useSmallStorage)} ${constraints.join(" ")}`.trim();
|
|
278
|
+
}).join(",\n");
|
|
279
|
+
if (this.schemaName) {
|
|
280
|
+
await this.setupSchema();
|
|
281
|
+
}
|
|
282
|
+
const checkTableRequest = this.pool.request();
|
|
283
|
+
checkTableRequest.input(
|
|
284
|
+
"tableName",
|
|
285
|
+
getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) }).replace(/[[\]]/g, "").split(".").pop()
|
|
286
|
+
);
|
|
287
|
+
const checkTableSql = `SELECT 1 AS found FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @tableName`;
|
|
288
|
+
checkTableRequest.input("schema", this.schemaName || "dbo");
|
|
289
|
+
const checkTableResult = await checkTableRequest.query(checkTableSql);
|
|
290
|
+
const tableExists = Array.isArray(checkTableResult.recordset) && checkTableResult.recordset.length > 0;
|
|
291
|
+
if (!tableExists) {
|
|
292
|
+
const createSql = `CREATE TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (
|
|
293
|
+
${columns}
|
|
294
|
+
)`;
|
|
295
|
+
await this.pool.request().query(createSql);
|
|
296
|
+
}
|
|
297
|
+
const columnCheckSql = `
|
|
298
|
+
SELECT 1 AS found
|
|
299
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
300
|
+
WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @tableName AND COLUMN_NAME = 'seq_id'
|
|
301
|
+
`;
|
|
302
|
+
const checkColumnRequest = this.pool.request();
|
|
303
|
+
checkColumnRequest.input("schema", this.schemaName || "dbo");
|
|
304
|
+
checkColumnRequest.input(
|
|
305
|
+
"tableName",
|
|
306
|
+
getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) }).replace(/[[\]]/g, "").split(".").pop()
|
|
307
|
+
);
|
|
308
|
+
const columnResult = await checkColumnRequest.query(columnCheckSql);
|
|
309
|
+
const columnExists = Array.isArray(columnResult.recordset) && columnResult.recordset.length > 0;
|
|
310
|
+
if (!columnExists) {
|
|
311
|
+
const alterSql = `ALTER TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} ADD seq_id BIGINT IDENTITY(1,1)`;
|
|
312
|
+
await this.pool.request().query(alterSql);
|
|
313
|
+
}
|
|
314
|
+
const schemaPrefix = this.schemaName ? `${this.schemaName}_` : "";
|
|
315
|
+
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
316
|
+
const constraintName = `${schemaPrefix}mastra_workflow_snapshot_workflow_name_run_id_key`;
|
|
317
|
+
const checkConstraintSql = `SELECT 1 AS found FROM sys.key_constraints WHERE name = @constraintName`;
|
|
318
|
+
const checkConstraintRequest = this.pool.request();
|
|
319
|
+
checkConstraintRequest.input("constraintName", constraintName);
|
|
320
|
+
const constraintResult = await checkConstraintRequest.query(checkConstraintSql);
|
|
321
|
+
const constraintExists = Array.isArray(constraintResult.recordset) && constraintResult.recordset.length > 0;
|
|
322
|
+
if (!constraintExists) {
|
|
323
|
+
const addConstraintSql = `ALTER TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} ADD CONSTRAINT [${constraintName}] UNIQUE ([workflow_name], [run_id])`;
|
|
324
|
+
await this.pool.request().query(addConstraintSql);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (tableName === TABLE_SPANS) {
|
|
328
|
+
await this.migrateSpansTable();
|
|
329
|
+
const pkConstraintName = `${schemaPrefix}mastra_ai_spans_traceid_spanid_pk`;
|
|
330
|
+
const checkPkRequest = this.pool.request();
|
|
331
|
+
checkPkRequest.input("constraintName", pkConstraintName);
|
|
332
|
+
const pkResult = await checkPkRequest.query(
|
|
333
|
+
`SELECT 1 AS found FROM sys.key_constraints WHERE name = @constraintName`
|
|
334
|
+
);
|
|
335
|
+
const pkExists = Array.isArray(pkResult.recordset) && pkResult.recordset.length > 0;
|
|
336
|
+
if (!pkExists) {
|
|
337
|
+
try {
|
|
338
|
+
const addPkSql = `ALTER TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} ADD CONSTRAINT [${pkConstraintName}] PRIMARY KEY ([traceId], [spanId])`;
|
|
339
|
+
await this.pool.request().query(addPkSql);
|
|
340
|
+
} catch (pkError) {
|
|
341
|
+
this.logger?.warn?.(`Failed to add composite primary key to spans table:`, pkError);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
} catch (error) {
|
|
346
|
+
throw new MastraError(
|
|
347
|
+
{
|
|
348
|
+
id: createStorageErrorId("MSSQL", "CREATE_TABLE", "FAILED"),
|
|
349
|
+
domain: ErrorDomain.STORAGE,
|
|
350
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
351
|
+
details: {
|
|
352
|
+
tableName
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
error
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Migrates the spans table schema from OLD_SPAN_SCHEMA to current SPAN_SCHEMA.
|
|
361
|
+
* This adds new columns that don't exist in old schema.
|
|
362
|
+
*/
|
|
363
|
+
async migrateSpansTable() {
|
|
364
|
+
const fullTableName = getTableName({ indexName: TABLE_SPANS, schemaName: getSchemaName(this.schemaName) });
|
|
365
|
+
const schema = TABLE_SCHEMAS[TABLE_SPANS];
|
|
366
|
+
try {
|
|
367
|
+
for (const [columnName, columnDef] of Object.entries(schema)) {
|
|
368
|
+
const columnExists = await this.hasColumn(TABLE_SPANS, columnName);
|
|
369
|
+
if (!columnExists) {
|
|
370
|
+
const parsedColumnName = parseSqlIdentifier(columnName, "column name");
|
|
371
|
+
const useLargeStorage = this.LARGE_DATA_COLUMNS.includes(columnName);
|
|
372
|
+
const useSmallStorage = this.COMPOSITE_INDEX_COLUMNS.includes(columnName);
|
|
373
|
+
const isIndexed = !!columnDef.primaryKey;
|
|
374
|
+
const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage, useSmallStorage);
|
|
375
|
+
const nullable = columnDef.nullable ? "" : "NOT NULL";
|
|
376
|
+
const defaultValue = !columnDef.nullable ? this.getDefaultValue(columnDef.type) : "";
|
|
377
|
+
const alterSql = `ALTER TABLE ${fullTableName} ADD [${parsedColumnName}] ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
378
|
+
await this.pool.request().query(alterSql);
|
|
379
|
+
this.logger?.debug?.(`Added column '${columnName}' to ${fullTableName}`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
this.logger?.info?.(`Migration completed for ${fullTableName}`);
|
|
383
|
+
} catch (error) {
|
|
384
|
+
this.logger?.warn?.(`Failed to migrate spans table ${fullTableName}:`, error);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Alters table schema to add columns if they don't exist
|
|
389
|
+
* @param tableName Name of the table
|
|
390
|
+
* @param schema Schema of the table
|
|
391
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
392
|
+
*/
|
|
393
|
+
async alterTable({
|
|
394
|
+
tableName,
|
|
395
|
+
schema,
|
|
396
|
+
ifNotExists
|
|
397
|
+
}) {
|
|
398
|
+
const fullTableName = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
|
|
399
|
+
try {
|
|
400
|
+
for (const columnName of ifNotExists) {
|
|
401
|
+
if (schema[columnName]) {
|
|
402
|
+
const columnCheckRequest = this.pool.request();
|
|
403
|
+
columnCheckRequest.input("tableName", fullTableName.replace(/[[\]]/g, "").split(".").pop());
|
|
404
|
+
columnCheckRequest.input("columnName", columnName);
|
|
405
|
+
columnCheckRequest.input("schema", this.schemaName || "dbo");
|
|
406
|
+
const checkSql = `SELECT 1 AS found FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @tableName AND COLUMN_NAME = @columnName`;
|
|
407
|
+
const checkResult = await columnCheckRequest.query(checkSql);
|
|
408
|
+
const columnExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
409
|
+
if (!columnExists) {
|
|
410
|
+
const columnDef = schema[columnName];
|
|
411
|
+
const useLargeStorage = this.LARGE_DATA_COLUMNS.includes(columnName);
|
|
412
|
+
const useSmallStorage = this.COMPOSITE_INDEX_COLUMNS.includes(columnName);
|
|
413
|
+
const isIndexed = !!columnDef.primaryKey;
|
|
414
|
+
const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage, useSmallStorage);
|
|
415
|
+
const nullable = columnDef.nullable ? "" : "NOT NULL";
|
|
416
|
+
const defaultValue = !columnDef.nullable ? this.getDefaultValue(columnDef.type) : "";
|
|
417
|
+
const parsedColumnName = parseSqlIdentifier(columnName, "column name");
|
|
418
|
+
const alterSql = `ALTER TABLE ${fullTableName} ADD [${parsedColumnName}] ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
419
|
+
await this.pool.request().query(alterSql);
|
|
420
|
+
this.logger?.debug?.(`Ensured column ${parsedColumnName} exists in table ${fullTableName}`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
} catch (error) {
|
|
425
|
+
throw new MastraError(
|
|
426
|
+
{
|
|
427
|
+
id: createStorageErrorId("MSSQL", "ALTER_TABLE", "FAILED"),
|
|
428
|
+
domain: ErrorDomain.STORAGE,
|
|
429
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
430
|
+
details: {
|
|
431
|
+
tableName
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
error
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
async load({ tableName, keys }) {
|
|
439
|
+
try {
|
|
440
|
+
const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, "column name"), value]);
|
|
441
|
+
const conditions = keyEntries.map(([key], i) => `[${key}] = @param${i}`).join(" AND ");
|
|
442
|
+
const sqlQuery = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
|
|
443
|
+
const request = this.pool.request();
|
|
444
|
+
keyEntries.forEach(([key, value], i) => {
|
|
445
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
446
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
447
|
+
request.input(`param${i}`, this.getMssqlType(tableName, key), null);
|
|
448
|
+
} else {
|
|
449
|
+
request.input(`param${i}`, preparedValue);
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
const resultSet = await request.query(sqlQuery);
|
|
453
|
+
const result = resultSet.recordset[0] || null;
|
|
454
|
+
if (!result) {
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
458
|
+
const snapshot = result;
|
|
459
|
+
if (typeof snapshot.snapshot === "string") {
|
|
460
|
+
snapshot.snapshot = JSON.parse(snapshot.snapshot);
|
|
461
|
+
}
|
|
462
|
+
return snapshot;
|
|
463
|
+
}
|
|
464
|
+
return result;
|
|
465
|
+
} catch (error) {
|
|
466
|
+
throw new MastraError(
|
|
467
|
+
{
|
|
468
|
+
id: createStorageErrorId("MSSQL", "LOAD", "FAILED"),
|
|
469
|
+
domain: ErrorDomain.STORAGE,
|
|
470
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
471
|
+
details: {
|
|
472
|
+
tableName
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
error
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
async batchInsert({ tableName, records }) {
|
|
480
|
+
const transaction = this.pool.transaction();
|
|
481
|
+
try {
|
|
482
|
+
await transaction.begin();
|
|
483
|
+
for (const record of records) {
|
|
484
|
+
await this.insert({ tableName, record, transaction });
|
|
485
|
+
}
|
|
486
|
+
await transaction.commit();
|
|
487
|
+
} catch (error) {
|
|
488
|
+
await transaction.rollback();
|
|
489
|
+
throw new MastraError(
|
|
490
|
+
{
|
|
491
|
+
id: createStorageErrorId("MSSQL", "BATCH_INSERT", "FAILED"),
|
|
492
|
+
domain: ErrorDomain.STORAGE,
|
|
493
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
494
|
+
details: {
|
|
495
|
+
tableName,
|
|
496
|
+
numberOfRecords: records.length
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
error
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
async dropTable({ tableName }) {
|
|
504
|
+
try {
|
|
505
|
+
const tableNameWithSchema = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
|
|
506
|
+
await this.pool.request().query(`DROP TABLE IF EXISTS ${tableNameWithSchema}`);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
throw new MastraError(
|
|
509
|
+
{
|
|
510
|
+
id: createStorageErrorId("MSSQL", "DROP_TABLE", "FAILED"),
|
|
511
|
+
domain: ErrorDomain.STORAGE,
|
|
512
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
513
|
+
details: {
|
|
514
|
+
tableName
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
error
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Prepares a value for database operations, handling Date objects and JSON serialization
|
|
523
|
+
*/
|
|
524
|
+
prepareValue(value, columnName, tableName) {
|
|
525
|
+
if (value === null || value === void 0) {
|
|
526
|
+
return value;
|
|
527
|
+
}
|
|
528
|
+
if (value instanceof Date) {
|
|
529
|
+
return value;
|
|
530
|
+
}
|
|
531
|
+
const schema = TABLE_SCHEMAS[tableName];
|
|
532
|
+
const columnSchema = schema?.[columnName];
|
|
533
|
+
if (columnSchema?.type === "boolean") {
|
|
534
|
+
return value ? 1 : 0;
|
|
535
|
+
}
|
|
536
|
+
if (columnSchema?.type === "jsonb") {
|
|
537
|
+
if (typeof value === "string") {
|
|
538
|
+
const trimmed = value.trim();
|
|
539
|
+
if (trimmed.length > 0) {
|
|
540
|
+
try {
|
|
541
|
+
JSON.parse(trimmed);
|
|
542
|
+
return trimmed;
|
|
543
|
+
} catch {
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return JSON.stringify(value);
|
|
547
|
+
}
|
|
548
|
+
if (typeof value === "bigint") {
|
|
549
|
+
return value.toString();
|
|
550
|
+
}
|
|
551
|
+
return JSON.stringify(value);
|
|
552
|
+
}
|
|
553
|
+
if (typeof value === "object") {
|
|
554
|
+
return JSON.stringify(value);
|
|
555
|
+
}
|
|
556
|
+
return value;
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Maps TABLE_SCHEMAS types to mssql param types (used when value is null)
|
|
560
|
+
*/
|
|
561
|
+
getMssqlType(tableName, columnName) {
|
|
562
|
+
const col = TABLE_SCHEMAS[tableName]?.[columnName];
|
|
563
|
+
switch (col?.type) {
|
|
564
|
+
case "text":
|
|
565
|
+
return sql.NVarChar;
|
|
566
|
+
case "timestamp":
|
|
567
|
+
return sql.DateTime2;
|
|
568
|
+
case "uuid":
|
|
569
|
+
return sql.UniqueIdentifier;
|
|
570
|
+
case "jsonb":
|
|
571
|
+
return sql.NVarChar;
|
|
572
|
+
case "integer":
|
|
573
|
+
return sql.Int;
|
|
574
|
+
case "bigint":
|
|
575
|
+
return sql.BigInt;
|
|
576
|
+
case "float":
|
|
577
|
+
return sql.Float;
|
|
578
|
+
case "boolean":
|
|
579
|
+
return sql.Bit;
|
|
580
|
+
default:
|
|
581
|
+
return sql.NVarChar;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Update a single record in the database
|
|
586
|
+
*/
|
|
587
|
+
async update({
|
|
588
|
+
tableName,
|
|
589
|
+
keys,
|
|
590
|
+
data,
|
|
591
|
+
transaction
|
|
592
|
+
}) {
|
|
593
|
+
try {
|
|
594
|
+
if (!data || Object.keys(data).length === 0) {
|
|
595
|
+
throw new MastraError({
|
|
596
|
+
id: createStorageErrorId("MSSQL", "UPDATE", "EMPTY_DATA"),
|
|
597
|
+
domain: ErrorDomain.STORAGE,
|
|
598
|
+
category: ErrorCategory.USER,
|
|
599
|
+
text: "Cannot update with empty data payload"
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
if (!keys || Object.keys(keys).length === 0) {
|
|
603
|
+
throw new MastraError({
|
|
604
|
+
id: createStorageErrorId("MSSQL", "UPDATE", "EMPTY_KEYS"),
|
|
605
|
+
domain: ErrorDomain.STORAGE,
|
|
606
|
+
category: ErrorCategory.USER,
|
|
607
|
+
text: "Cannot update without keys to identify records"
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
const setClauses = [];
|
|
611
|
+
const request = transaction ? transaction.request() : this.pool.request();
|
|
612
|
+
let paramIndex = 0;
|
|
613
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
614
|
+
const parsedKey = parseSqlIdentifier(key, "column name");
|
|
615
|
+
const paramName = `set${paramIndex++}`;
|
|
616
|
+
setClauses.push(`[${parsedKey}] = @${paramName}`);
|
|
617
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
618
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
619
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
620
|
+
} else {
|
|
621
|
+
request.input(paramName, preparedValue);
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
const whereConditions = [];
|
|
625
|
+
Object.entries(keys).forEach(([key, value]) => {
|
|
626
|
+
const parsedKey = parseSqlIdentifier(key, "column name");
|
|
627
|
+
const paramName = `where${paramIndex++}`;
|
|
628
|
+
whereConditions.push(`[${parsedKey}] = @${paramName}`);
|
|
629
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
630
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
631
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
632
|
+
} else {
|
|
633
|
+
request.input(paramName, preparedValue);
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
const tableName_ = getTableName({
|
|
637
|
+
indexName: tableName,
|
|
638
|
+
schemaName: getSchemaName(this.schemaName)
|
|
639
|
+
});
|
|
640
|
+
const updateSql = `UPDATE ${tableName_} SET ${setClauses.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
|
|
641
|
+
await request.query(updateSql);
|
|
642
|
+
} catch (error) {
|
|
643
|
+
throw new MastraError(
|
|
644
|
+
{
|
|
645
|
+
id: createStorageErrorId("MSSQL", "UPDATE", "FAILED"),
|
|
646
|
+
domain: ErrorDomain.STORAGE,
|
|
647
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
648
|
+
details: {
|
|
649
|
+
tableName
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
error
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Update multiple records in a single batch transaction
|
|
658
|
+
*/
|
|
659
|
+
async batchUpdate({
|
|
660
|
+
tableName,
|
|
661
|
+
updates
|
|
662
|
+
}) {
|
|
663
|
+
const transaction = this.pool.transaction();
|
|
664
|
+
try {
|
|
665
|
+
await transaction.begin();
|
|
666
|
+
for (const { keys, data } of updates) {
|
|
667
|
+
await this.update({ tableName, keys, data, transaction });
|
|
668
|
+
}
|
|
669
|
+
await transaction.commit();
|
|
670
|
+
} catch (error) {
|
|
671
|
+
await transaction.rollback();
|
|
672
|
+
throw new MastraError(
|
|
673
|
+
{
|
|
674
|
+
id: createStorageErrorId("MSSQL", "BATCH_UPDATE", "FAILED"),
|
|
675
|
+
domain: ErrorDomain.STORAGE,
|
|
676
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
677
|
+
details: {
|
|
678
|
+
tableName,
|
|
679
|
+
numberOfRecords: updates.length
|
|
680
|
+
}
|
|
681
|
+
},
|
|
682
|
+
error
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Delete multiple records by keys
|
|
688
|
+
*/
|
|
689
|
+
async batchDelete({ tableName, keys }) {
|
|
690
|
+
if (keys.length === 0) {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
const tableName_ = getTableName({
|
|
694
|
+
indexName: tableName,
|
|
695
|
+
schemaName: getSchemaName(this.schemaName)
|
|
696
|
+
});
|
|
697
|
+
const transaction = this.pool.transaction();
|
|
698
|
+
try {
|
|
699
|
+
await transaction.begin();
|
|
700
|
+
for (const keySet of keys) {
|
|
701
|
+
const conditions = [];
|
|
702
|
+
const request = transaction.request();
|
|
703
|
+
let paramIndex = 0;
|
|
704
|
+
Object.entries(keySet).forEach(([key, value]) => {
|
|
705
|
+
const parsedKey = parseSqlIdentifier(key, "column name");
|
|
706
|
+
const paramName = `p${paramIndex++}`;
|
|
707
|
+
conditions.push(`[${parsedKey}] = @${paramName}`);
|
|
708
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
709
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
710
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
711
|
+
} else {
|
|
712
|
+
request.input(paramName, preparedValue);
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
const deleteSql = `DELETE FROM ${tableName_} WHERE ${conditions.join(" AND ")}`;
|
|
716
|
+
await request.query(deleteSql);
|
|
717
|
+
}
|
|
718
|
+
await transaction.commit();
|
|
719
|
+
} catch (error) {
|
|
720
|
+
await transaction.rollback();
|
|
721
|
+
throw new MastraError(
|
|
722
|
+
{
|
|
723
|
+
id: createStorageErrorId("MSSQL", "BATCH_DELETE", "FAILED"),
|
|
724
|
+
domain: ErrorDomain.STORAGE,
|
|
725
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
726
|
+
details: {
|
|
727
|
+
tableName,
|
|
728
|
+
numberOfRecords: keys.length
|
|
729
|
+
}
|
|
730
|
+
},
|
|
731
|
+
error
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Create a new index on a table
|
|
737
|
+
*/
|
|
738
|
+
async createIndex(options) {
|
|
739
|
+
try {
|
|
740
|
+
const { name, table, columns, unique = false, where } = options;
|
|
741
|
+
const schemaName = this.schemaName || "dbo";
|
|
742
|
+
const fullTableName = getTableName({
|
|
743
|
+
indexName: table,
|
|
744
|
+
schemaName: getSchemaName(this.schemaName)
|
|
745
|
+
});
|
|
746
|
+
const indexNameSafe = parseSqlIdentifier(name, "index name");
|
|
747
|
+
const checkRequest = this.pool.request();
|
|
748
|
+
checkRequest.input("indexName", indexNameSafe);
|
|
749
|
+
checkRequest.input("schemaName", schemaName);
|
|
750
|
+
checkRequest.input("tableName", table);
|
|
751
|
+
const indexExists = await checkRequest.query(`
|
|
752
|
+
SELECT 1 as found
|
|
753
|
+
FROM sys.indexes i
|
|
754
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
755
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
756
|
+
WHERE i.name = @indexName
|
|
757
|
+
AND s.name = @schemaName
|
|
758
|
+
AND t.name = @tableName
|
|
759
|
+
`);
|
|
760
|
+
if (indexExists.recordset && indexExists.recordset.length > 0) {
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
const uniqueStr = unique ? "UNIQUE " : "";
|
|
764
|
+
const columnsStr = columns.map((col) => {
|
|
765
|
+
if (col.includes(" DESC") || col.includes(" ASC")) {
|
|
766
|
+
const [colName, ...modifiers] = col.split(" ");
|
|
767
|
+
if (!colName) {
|
|
768
|
+
throw new Error(`Invalid column specification: ${col}`);
|
|
769
|
+
}
|
|
770
|
+
return `[${parseSqlIdentifier(colName, "column name")}] ${modifiers.join(" ")}`;
|
|
771
|
+
}
|
|
772
|
+
return `[${parseSqlIdentifier(col, "column name")}]`;
|
|
773
|
+
}).join(", ");
|
|
774
|
+
const whereStr = where ? ` WHERE ${where}` : "";
|
|
775
|
+
const createIndexSql = `CREATE ${uniqueStr}INDEX [${indexNameSafe}] ON ${fullTableName} (${columnsStr})${whereStr}`;
|
|
776
|
+
await this.pool.request().query(createIndexSql);
|
|
777
|
+
} catch (error) {
|
|
778
|
+
throw new MastraError(
|
|
779
|
+
{
|
|
780
|
+
id: createStorageErrorId("MSSQL", "INDEX_CREATE", "FAILED"),
|
|
781
|
+
domain: ErrorDomain.STORAGE,
|
|
782
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
783
|
+
details: {
|
|
784
|
+
indexName: options.name,
|
|
785
|
+
tableName: options.table
|
|
786
|
+
}
|
|
787
|
+
},
|
|
788
|
+
error
|
|
789
|
+
);
|
|
25
790
|
}
|
|
26
791
|
}
|
|
27
|
-
|
|
792
|
+
/**
|
|
793
|
+
* Drop an existing index
|
|
794
|
+
*/
|
|
795
|
+
async dropIndex(indexName) {
|
|
28
796
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
797
|
+
const schemaName = this.schemaName || "dbo";
|
|
798
|
+
const indexNameSafe = parseSqlIdentifier(indexName, "index name");
|
|
799
|
+
const checkRequest = this.pool.request();
|
|
800
|
+
checkRequest.input("indexName", indexNameSafe);
|
|
801
|
+
checkRequest.input("schemaName", schemaName);
|
|
802
|
+
const result = await checkRequest.query(`
|
|
803
|
+
SELECT t.name as table_name
|
|
804
|
+
FROM sys.indexes i
|
|
805
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
806
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
807
|
+
WHERE i.name = @indexName
|
|
808
|
+
AND s.name = @schemaName
|
|
809
|
+
`);
|
|
810
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
if (result.recordset.length > 1) {
|
|
814
|
+
const tables = result.recordset.map((r) => r.table_name).join(", ");
|
|
815
|
+
throw new MastraError({
|
|
816
|
+
id: createStorageErrorId("MSSQL", "INDEX", "AMBIGUOUS"),
|
|
817
|
+
domain: ErrorDomain.STORAGE,
|
|
818
|
+
category: ErrorCategory.USER,
|
|
819
|
+
text: `Index "${indexNameSafe}" exists on multiple tables (${tables}) in schema "${schemaName}". Please drop indexes manually or ensure unique index names.`
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
const tableName = result.recordset[0].table_name;
|
|
823
|
+
const fullTableName = getTableName({
|
|
824
|
+
indexName: tableName,
|
|
825
|
+
schemaName: getSchemaName(this.schemaName)
|
|
826
|
+
});
|
|
827
|
+
const dropSql = `DROP INDEX [${indexNameSafe}] ON ${fullTableName}`;
|
|
828
|
+
await this.pool.request().query(dropSql);
|
|
829
|
+
} catch (error) {
|
|
830
|
+
throw new MastraError(
|
|
831
|
+
{
|
|
832
|
+
id: createStorageErrorId("MSSQL", "INDEX_DROP", "FAILED"),
|
|
833
|
+
domain: ErrorDomain.STORAGE,
|
|
834
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
835
|
+
details: {
|
|
836
|
+
indexName
|
|
837
|
+
}
|
|
838
|
+
},
|
|
839
|
+
error
|
|
840
|
+
);
|
|
31
841
|
}
|
|
32
842
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
result: resultValue,
|
|
38
|
-
metricName: row.metric_name,
|
|
39
|
-
instructions: row.instructions,
|
|
40
|
-
testInfo: testInfoValue,
|
|
41
|
-
globalRunId: row.global_run_id,
|
|
42
|
-
runId: row.run_id,
|
|
43
|
-
createdAt: row.created_at
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
var LegacyEvalsMSSQL = class extends LegacyEvalsStorage {
|
|
47
|
-
pool;
|
|
48
|
-
schema;
|
|
49
|
-
constructor({ pool, schema }) {
|
|
50
|
-
super();
|
|
51
|
-
this.pool = pool;
|
|
52
|
-
this.schema = schema;
|
|
53
|
-
}
|
|
54
|
-
/** @deprecated use getEvals instead */
|
|
55
|
-
async getEvalsByAgentName(agentName, type) {
|
|
843
|
+
/**
|
|
844
|
+
* List indexes for a specific table or all tables
|
|
845
|
+
*/
|
|
846
|
+
async listIndexes(tableName) {
|
|
56
847
|
try {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
query += " AND test_info IS NOT NULL AND JSON_VALUE(test_info, '$.testPath') IS NOT NULL";
|
|
60
|
-
} else if (type === "live") {
|
|
61
|
-
query += " AND (test_info IS NULL OR JSON_VALUE(test_info, '$.testPath') IS NULL)";
|
|
62
|
-
}
|
|
63
|
-
query += " ORDER BY created_at DESC";
|
|
848
|
+
const schemaName = this.schemaName || "dbo";
|
|
849
|
+
let query;
|
|
64
850
|
const request = this.pool.request();
|
|
65
|
-
request.input("
|
|
851
|
+
request.input("schemaName", schemaName);
|
|
852
|
+
if (tableName) {
|
|
853
|
+
query = `
|
|
854
|
+
SELECT
|
|
855
|
+
i.name as name,
|
|
856
|
+
o.name as [table],
|
|
857
|
+
i.is_unique as is_unique,
|
|
858
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
|
|
859
|
+
FROM sys.indexes i
|
|
860
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
861
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
862
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
863
|
+
WHERE sch.name = @schemaName
|
|
864
|
+
AND o.name = @tableName
|
|
865
|
+
AND i.name IS NOT NULL
|
|
866
|
+
GROUP BY i.name, o.name, i.is_unique
|
|
867
|
+
`;
|
|
868
|
+
request.input("tableName", tableName);
|
|
869
|
+
} else {
|
|
870
|
+
query = `
|
|
871
|
+
SELECT
|
|
872
|
+
i.name as name,
|
|
873
|
+
o.name as [table],
|
|
874
|
+
i.is_unique as is_unique,
|
|
875
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
|
|
876
|
+
FROM sys.indexes i
|
|
877
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
878
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
879
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
880
|
+
WHERE sch.name = @schemaName
|
|
881
|
+
AND i.name IS NOT NULL
|
|
882
|
+
GROUP BY i.name, o.name, i.is_unique
|
|
883
|
+
`;
|
|
884
|
+
}
|
|
66
885
|
const result = await request.query(query);
|
|
67
|
-
const
|
|
68
|
-
|
|
886
|
+
const indexes = [];
|
|
887
|
+
for (const row of result.recordset) {
|
|
888
|
+
const colRequest = this.pool.request();
|
|
889
|
+
colRequest.input("indexName", row.name);
|
|
890
|
+
colRequest.input("schemaName", schemaName);
|
|
891
|
+
const colResult = await colRequest.query(`
|
|
892
|
+
SELECT c.name as column_name
|
|
893
|
+
FROM sys.indexes i
|
|
894
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
895
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
896
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
897
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
898
|
+
WHERE i.name = @indexName
|
|
899
|
+
AND s.name = @schemaName
|
|
900
|
+
ORDER BY ic.key_ordinal
|
|
901
|
+
`);
|
|
902
|
+
indexes.push({
|
|
903
|
+
name: row.name,
|
|
904
|
+
table: row.table,
|
|
905
|
+
columns: colResult.recordset.map((c) => c.column_name),
|
|
906
|
+
unique: row.is_unique || false,
|
|
907
|
+
size: row.size || "0 MB",
|
|
908
|
+
definition: ""
|
|
909
|
+
// MSSQL doesn't store definition like PG
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
return indexes;
|
|
69
913
|
} catch (error) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (type === "test") {
|
|
88
|
-
where.push("test_info IS NOT NULL AND JSON_VALUE(test_info, '$.testPath') IS NOT NULL");
|
|
89
|
-
} else if (type === "live") {
|
|
90
|
-
where.push("(test_info IS NULL OR JSON_VALUE(test_info, '$.testPath') IS NULL)");
|
|
91
|
-
}
|
|
92
|
-
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
93
|
-
where.push(`[created_at] >= @fromDate`);
|
|
94
|
-
params[`fromDate`] = fromDate.toISOString();
|
|
95
|
-
}
|
|
96
|
-
if (toDate instanceof Date && !isNaN(toDate.getTime())) {
|
|
97
|
-
where.push(`[created_at] <= @toDate`);
|
|
98
|
-
params[`toDate`] = toDate.toISOString();
|
|
99
|
-
}
|
|
100
|
-
const whereClause = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
|
|
101
|
-
const tableName = getTableName({ indexName: TABLE_EVALS, schemaName: getSchemaName(this.schema) });
|
|
102
|
-
const offset = page * perPage;
|
|
103
|
-
const countQuery = `SELECT COUNT(*) as total FROM ${tableName} ${whereClause}`;
|
|
104
|
-
const dataQuery = `SELECT * FROM ${tableName} ${whereClause} ORDER BY seq_id DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
914
|
+
throw new MastraError(
|
|
915
|
+
{
|
|
916
|
+
id: createStorageErrorId("MSSQL", "INDEX_LIST", "FAILED"),
|
|
917
|
+
domain: ErrorDomain.STORAGE,
|
|
918
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
919
|
+
details: tableName ? {
|
|
920
|
+
tableName
|
|
921
|
+
} : {}
|
|
922
|
+
},
|
|
923
|
+
error
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Get detailed statistics for a specific index
|
|
929
|
+
*/
|
|
930
|
+
async describeIndex(indexName) {
|
|
105
931
|
try {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
932
|
+
const schemaName = this.schemaName || "dbo";
|
|
933
|
+
const request = this.pool.request();
|
|
934
|
+
request.input("indexName", indexName);
|
|
935
|
+
request.input("schemaName", schemaName);
|
|
936
|
+
const query = `
|
|
937
|
+
SELECT
|
|
938
|
+
i.name as name,
|
|
939
|
+
o.name as [table],
|
|
940
|
+
i.is_unique as is_unique,
|
|
941
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size,
|
|
942
|
+
i.type_desc as method,
|
|
943
|
+
ISNULL(us.user_scans, 0) as scans,
|
|
944
|
+
ISNULL(us.user_seeks + us.user_scans, 0) as tuples_read,
|
|
945
|
+
ISNULL(us.user_lookups, 0) as tuples_fetched
|
|
946
|
+
FROM sys.indexes i
|
|
947
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
948
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
949
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
950
|
+
LEFT JOIN sys.dm_db_index_usage_stats us ON i.object_id = us.object_id AND i.index_id = us.index_id
|
|
951
|
+
WHERE i.name = @indexName
|
|
952
|
+
AND sch.name = @schemaName
|
|
953
|
+
GROUP BY i.name, o.name, i.is_unique, i.type_desc, us.user_seeks, us.user_scans, us.user_lookups
|
|
954
|
+
`;
|
|
955
|
+
const result = await request.query(query);
|
|
956
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
957
|
+
throw new Error(`Index "${indexName}" not found in schema "${schemaName}"`);
|
|
124
958
|
}
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
959
|
+
const row = result.recordset[0];
|
|
960
|
+
const colRequest = this.pool.request();
|
|
961
|
+
colRequest.input("indexName", indexName);
|
|
962
|
+
colRequest.input("schemaName", schemaName);
|
|
963
|
+
const colResult = await colRequest.query(`
|
|
964
|
+
SELECT c.name as column_name
|
|
965
|
+
FROM sys.indexes i
|
|
966
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
967
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
968
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
969
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
970
|
+
WHERE i.name = @indexName
|
|
971
|
+
AND s.name = @schemaName
|
|
972
|
+
ORDER BY ic.key_ordinal
|
|
973
|
+
`);
|
|
137
974
|
return {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
975
|
+
name: row.name,
|
|
976
|
+
table: row.table,
|
|
977
|
+
columns: colResult.recordset.map((c) => c.column_name),
|
|
978
|
+
unique: row.is_unique || false,
|
|
979
|
+
size: row.size || "0 MB",
|
|
980
|
+
definition: "",
|
|
981
|
+
method: row.method?.toLowerCase() || "nonclustered",
|
|
982
|
+
scans: Number(row.scans) || 0,
|
|
983
|
+
tuples_read: Number(row.tuples_read) || 0,
|
|
984
|
+
tuples_fetched: Number(row.tuples_fetched) || 0
|
|
143
985
|
};
|
|
144
986
|
} catch (error) {
|
|
145
|
-
|
|
987
|
+
throw new MastraError(
|
|
146
988
|
{
|
|
147
|
-
id: "
|
|
989
|
+
id: createStorageErrorId("MSSQL", "INDEX_DESCRIBE", "FAILED"),
|
|
148
990
|
domain: ErrorDomain.STORAGE,
|
|
149
991
|
category: ErrorCategory.THIRD_PARTY,
|
|
150
992
|
details: {
|
|
151
|
-
|
|
152
|
-
type: type || "all",
|
|
153
|
-
page,
|
|
154
|
-
perPage
|
|
993
|
+
indexName
|
|
155
994
|
}
|
|
156
995
|
},
|
|
157
996
|
error
|
|
158
997
|
);
|
|
159
|
-
this.logger?.error?.(mastraError.toString());
|
|
160
|
-
this.logger?.trackException(mastraError);
|
|
161
|
-
throw mastraError;
|
|
162
998
|
}
|
|
163
999
|
}
|
|
164
1000
|
};
|
|
165
|
-
|
|
1001
|
+
function getSchemaName2(schema) {
|
|
1002
|
+
return schema ? `[${parseSqlIdentifier(schema, "schema name")}]` : void 0;
|
|
1003
|
+
}
|
|
1004
|
+
function getTableName2({ indexName, schemaName }) {
|
|
1005
|
+
const parsedIndexName = parseSqlIdentifier(indexName, "index name");
|
|
1006
|
+
const quotedIndexName = `[${parsedIndexName}]`;
|
|
1007
|
+
const quotedSchemaName = schemaName;
|
|
1008
|
+
return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
|
|
1009
|
+
}
|
|
1010
|
+
function buildDateRangeFilter(dateRange, fieldName) {
|
|
1011
|
+
const filters = {};
|
|
1012
|
+
if (dateRange?.start) {
|
|
1013
|
+
const suffix = dateRange.startExclusive ? "_gt" : "_gte";
|
|
1014
|
+
filters[`${fieldName}${suffix}`] = dateRange.start;
|
|
1015
|
+
}
|
|
1016
|
+
if (dateRange?.end) {
|
|
1017
|
+
const suffix = dateRange.endExclusive ? "_lt" : "_lte";
|
|
1018
|
+
filters[`${fieldName}${suffix}`] = dateRange.end;
|
|
1019
|
+
}
|
|
1020
|
+
return filters;
|
|
1021
|
+
}
|
|
1022
|
+
function isInOperator(value) {
|
|
1023
|
+
return typeof value === "object" && value !== null && "$in" in value && Array.isArray(value.$in);
|
|
1024
|
+
}
|
|
1025
|
+
function prepareWhereClause(filters, _schema) {
|
|
1026
|
+
const conditions = [];
|
|
1027
|
+
const params = {};
|
|
1028
|
+
let paramIndex = 1;
|
|
1029
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
1030
|
+
if (value === void 0) return;
|
|
1031
|
+
if (key.endsWith("_gte")) {
|
|
1032
|
+
const paramName = `p${paramIndex++}`;
|
|
1033
|
+
const fieldName = key.slice(0, -4);
|
|
1034
|
+
conditions.push(`[${parseSqlIdentifier(fieldName, "field name")}] >= @${paramName}`);
|
|
1035
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
1036
|
+
} else if (key.endsWith("_gt")) {
|
|
1037
|
+
const paramName = `p${paramIndex++}`;
|
|
1038
|
+
const fieldName = key.slice(0, -3);
|
|
1039
|
+
conditions.push(`[${parseSqlIdentifier(fieldName, "field name")}] > @${paramName}`);
|
|
1040
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
1041
|
+
} else if (key.endsWith("_lte")) {
|
|
1042
|
+
const paramName = `p${paramIndex++}`;
|
|
1043
|
+
const fieldName = key.slice(0, -4);
|
|
1044
|
+
conditions.push(`[${parseSqlIdentifier(fieldName, "field name")}] <= @${paramName}`);
|
|
1045
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
1046
|
+
} else if (key.endsWith("_lt")) {
|
|
1047
|
+
const paramName = `p${paramIndex++}`;
|
|
1048
|
+
const fieldName = key.slice(0, -3);
|
|
1049
|
+
conditions.push(`[${parseSqlIdentifier(fieldName, "field name")}] < @${paramName}`);
|
|
1050
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
1051
|
+
} else if (value === null) {
|
|
1052
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] IS NULL`);
|
|
1053
|
+
} else if (isInOperator(value)) {
|
|
1054
|
+
const inValues = value.$in;
|
|
1055
|
+
if (inValues.length === 0) {
|
|
1056
|
+
conditions.push("1 = 0");
|
|
1057
|
+
} else if (inValues.length === 1) {
|
|
1058
|
+
const paramName = `p${paramIndex++}`;
|
|
1059
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] = @${paramName}`);
|
|
1060
|
+
params[paramName] = inValues[0] instanceof Date ? inValues[0].toISOString() : inValues[0];
|
|
1061
|
+
} else {
|
|
1062
|
+
const inParamNames = [];
|
|
1063
|
+
for (const item of inValues) {
|
|
1064
|
+
const paramName = `p${paramIndex++}`;
|
|
1065
|
+
inParamNames.push(`@${paramName}`);
|
|
1066
|
+
params[paramName] = item instanceof Date ? item.toISOString() : item;
|
|
1067
|
+
}
|
|
1068
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] IN (${inParamNames.join(", ")})`);
|
|
1069
|
+
}
|
|
1070
|
+
} else if (Array.isArray(value)) {
|
|
1071
|
+
if (value.length === 0) {
|
|
1072
|
+
conditions.push("1 = 0");
|
|
1073
|
+
} else if (value.length === 1) {
|
|
1074
|
+
const paramName = `p${paramIndex++}`;
|
|
1075
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] = @${paramName}`);
|
|
1076
|
+
params[paramName] = value[0] instanceof Date ? value[0].toISOString() : value[0];
|
|
1077
|
+
} else {
|
|
1078
|
+
const inParamNames = [];
|
|
1079
|
+
for (const item of value) {
|
|
1080
|
+
const paramName = `p${paramIndex++}`;
|
|
1081
|
+
inParamNames.push(`@${paramName}`);
|
|
1082
|
+
params[paramName] = item instanceof Date ? item.toISOString() : item;
|
|
1083
|
+
}
|
|
1084
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] IN (${inParamNames.join(", ")})`);
|
|
1085
|
+
}
|
|
1086
|
+
} else {
|
|
1087
|
+
const paramName = `p${paramIndex++}`;
|
|
1088
|
+
conditions.push(`[${parseSqlIdentifier(key, "field name")}] = @${paramName}`);
|
|
1089
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
1092
|
+
return {
|
|
1093
|
+
sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
|
|
1094
|
+
params
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
function transformFromSqlRow({
|
|
1098
|
+
tableName,
|
|
1099
|
+
sqlRow
|
|
1100
|
+
}) {
|
|
1101
|
+
const schema = TABLE_SCHEMAS[tableName];
|
|
1102
|
+
const result = {};
|
|
1103
|
+
Object.entries(sqlRow).forEach(([key, value]) => {
|
|
1104
|
+
const columnSchema = schema?.[key];
|
|
1105
|
+
if (columnSchema?.type === "jsonb" && typeof value === "string") {
|
|
1106
|
+
try {
|
|
1107
|
+
result[key] = JSON.parse(value);
|
|
1108
|
+
} catch {
|
|
1109
|
+
result[key] = value;
|
|
1110
|
+
}
|
|
1111
|
+
} else if (columnSchema?.type === "timestamp" && value && typeof value === "string") {
|
|
1112
|
+
result[key] = new Date(value);
|
|
1113
|
+
} else if (columnSchema?.type === "timestamp" && value instanceof Date) {
|
|
1114
|
+
result[key] = value;
|
|
1115
|
+
} else if (columnSchema?.type === "boolean") {
|
|
1116
|
+
result[key] = Boolean(value);
|
|
1117
|
+
} else {
|
|
1118
|
+
result[key] = value;
|
|
1119
|
+
}
|
|
1120
|
+
});
|
|
1121
|
+
return result;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// src/storage/domains/memory/index.ts
|
|
1125
|
+
var MemoryMSSQL = class _MemoryMSSQL extends MemoryStorage {
|
|
166
1126
|
pool;
|
|
167
1127
|
schema;
|
|
168
|
-
|
|
1128
|
+
db;
|
|
1129
|
+
needsConnect;
|
|
1130
|
+
skipDefaultIndexes;
|
|
1131
|
+
indexes;
|
|
1132
|
+
/** Tables managed by this domain */
|
|
1133
|
+
static MANAGED_TABLES = [TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES];
|
|
169
1134
|
_parseAndFormatMessages(messages, format) {
|
|
170
1135
|
const messagesWithParsedContent = messages.map((message) => {
|
|
171
1136
|
if (typeof message.content === "string") {
|
|
@@ -179,32 +1144,97 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
179
1144
|
});
|
|
180
1145
|
const cleanMessages = messagesWithParsedContent.map(({ seq_id, ...rest }) => rest);
|
|
181
1146
|
const list = new MessageList().add(cleanMessages, "memory");
|
|
182
|
-
return format === "v2" ? list.get.all.
|
|
1147
|
+
return format === "v2" ? list.get.all.db() : list.get.all.v1();
|
|
183
1148
|
}
|
|
184
|
-
constructor({
|
|
185
|
-
pool,
|
|
186
|
-
schema,
|
|
187
|
-
operations
|
|
188
|
-
}) {
|
|
1149
|
+
constructor(config) {
|
|
189
1150
|
super();
|
|
1151
|
+
const { pool, schemaName, skipDefaultIndexes, indexes, needsConnect } = resolveMssqlConfig(config);
|
|
190
1152
|
this.pool = pool;
|
|
191
|
-
this.schema =
|
|
192
|
-
this.
|
|
1153
|
+
this.schema = schemaName;
|
|
1154
|
+
this.db = new MssqlDB({ pool, schemaName, skipDefaultIndexes });
|
|
1155
|
+
this.needsConnect = needsConnect;
|
|
1156
|
+
this.skipDefaultIndexes = skipDefaultIndexes;
|
|
1157
|
+
this.indexes = indexes?.filter((idx) => _MemoryMSSQL.MANAGED_TABLES.includes(idx.table));
|
|
1158
|
+
}
|
|
1159
|
+
async init() {
|
|
1160
|
+
if (this.needsConnect) {
|
|
1161
|
+
await this.pool.connect();
|
|
1162
|
+
this.needsConnect = false;
|
|
1163
|
+
}
|
|
1164
|
+
await this.db.createTable({ tableName: TABLE_THREADS, schema: TABLE_SCHEMAS[TABLE_THREADS] });
|
|
1165
|
+
await this.db.createTable({ tableName: TABLE_MESSAGES, schema: TABLE_SCHEMAS[TABLE_MESSAGES] });
|
|
1166
|
+
await this.db.createTable({ tableName: TABLE_RESOURCES, schema: TABLE_SCHEMAS[TABLE_RESOURCES] });
|
|
1167
|
+
await this.createDefaultIndexes();
|
|
1168
|
+
await this.createCustomIndexes();
|
|
1169
|
+
}
|
|
1170
|
+
/**
|
|
1171
|
+
* Returns default index definitions for the memory domain tables.
|
|
1172
|
+
* IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
|
|
1173
|
+
*/
|
|
1174
|
+
getDefaultIndexDefinitions() {
|
|
1175
|
+
const schemaPrefix = this.schema ? `${this.schema}_` : "";
|
|
1176
|
+
return [
|
|
1177
|
+
{
|
|
1178
|
+
name: `${schemaPrefix}mastra_threads_resourceid_seqid_idx`,
|
|
1179
|
+
table: TABLE_THREADS,
|
|
1180
|
+
columns: ["resourceId", "seq_id DESC"]
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
name: `${schemaPrefix}mastra_messages_thread_id_seqid_idx`,
|
|
1184
|
+
table: TABLE_MESSAGES,
|
|
1185
|
+
columns: ["thread_id", "seq_id DESC"]
|
|
1186
|
+
}
|
|
1187
|
+
];
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Creates default indexes for optimal query performance.
|
|
1191
|
+
*/
|
|
1192
|
+
async createDefaultIndexes() {
|
|
1193
|
+
if (this.skipDefaultIndexes) {
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
for (const indexDef of this.getDefaultIndexDefinitions()) {
|
|
1197
|
+
try {
|
|
1198
|
+
await this.db.createIndex(indexDef);
|
|
1199
|
+
} catch (error) {
|
|
1200
|
+
this.logger?.warn?.(`Failed to create index ${indexDef.name}:`, error);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* Creates custom user-defined indexes for this domain's tables.
|
|
1206
|
+
*/
|
|
1207
|
+
async createCustomIndexes() {
|
|
1208
|
+
if (!this.indexes || this.indexes.length === 0) {
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
for (const indexDef of this.indexes) {
|
|
1212
|
+
try {
|
|
1213
|
+
await this.db.createIndex(indexDef);
|
|
1214
|
+
} catch (error) {
|
|
1215
|
+
this.logger?.warn?.(`Failed to create custom index ${indexDef.name}:`, error);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
async dangerouslyClearAll() {
|
|
1220
|
+
await this.db.clearTable({ tableName: TABLE_MESSAGES });
|
|
1221
|
+
await this.db.clearTable({ tableName: TABLE_THREADS });
|
|
1222
|
+
await this.db.clearTable({ tableName: TABLE_RESOURCES });
|
|
193
1223
|
}
|
|
194
1224
|
async getThreadById({ threadId }) {
|
|
195
1225
|
try {
|
|
196
|
-
const
|
|
1226
|
+
const sql5 = `SELECT
|
|
197
1227
|
id,
|
|
198
1228
|
[resourceId],
|
|
199
1229
|
title,
|
|
200
1230
|
metadata,
|
|
201
1231
|
[createdAt],
|
|
202
1232
|
[updatedAt]
|
|
203
|
-
FROM ${
|
|
1233
|
+
FROM ${getTableName2({ indexName: TABLE_THREADS, schemaName: getSchemaName2(this.schema) })}
|
|
204
1234
|
WHERE id = @threadId`;
|
|
205
1235
|
const request = this.pool.request();
|
|
206
1236
|
request.input("threadId", threadId);
|
|
207
|
-
const resultSet = await request.query(
|
|
1237
|
+
const resultSet = await request.query(sql5);
|
|
208
1238
|
const thread = resultSet.recordset[0] || null;
|
|
209
1239
|
if (!thread) {
|
|
210
1240
|
return null;
|
|
@@ -218,7 +1248,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
218
1248
|
} catch (error) {
|
|
219
1249
|
throw new MastraError(
|
|
220
1250
|
{
|
|
221
|
-
id: "
|
|
1251
|
+
id: createStorageErrorId("MSSQL", "GET_THREAD_BY_ID", "FAILED"),
|
|
222
1252
|
domain: ErrorDomain.STORAGE,
|
|
223
1253
|
category: ErrorCategory.THIRD_PARTY,
|
|
224
1254
|
details: {
|
|
@@ -229,12 +1259,25 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
229
1259
|
);
|
|
230
1260
|
}
|
|
231
1261
|
}
|
|
232
|
-
async
|
|
233
|
-
const { resourceId, page = 0, perPage: perPageInput, orderBy
|
|
1262
|
+
async listThreadsByResourceId(args) {
|
|
1263
|
+
const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
|
|
1264
|
+
if (page < 0) {
|
|
1265
|
+
throw new MastraError({
|
|
1266
|
+
id: createStorageErrorId("MSSQL", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
|
|
1267
|
+
domain: ErrorDomain.STORAGE,
|
|
1268
|
+
category: ErrorCategory.USER,
|
|
1269
|
+
text: "Page number must be non-negative",
|
|
1270
|
+
details: {
|
|
1271
|
+
resourceId,
|
|
1272
|
+
page
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1277
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1278
|
+
const { field, direction } = this.parseOrderBy(orderBy);
|
|
234
1279
|
try {
|
|
235
|
-
const
|
|
236
|
-
const currentOffset = page * perPage;
|
|
237
|
-
const baseQuery = `FROM ${getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) })} WHERE [resourceId] = @resourceId`;
|
|
1280
|
+
const baseQuery = `FROM ${getTableName2({ indexName: TABLE_THREADS, schemaName: getSchemaName2(this.schema) })} WHERE [resourceId] = @resourceId`;
|
|
238
1281
|
const countQuery = `SELECT COUNT(*) as count ${baseQuery}`;
|
|
239
1282
|
const countRequest = this.pool.request();
|
|
240
1283
|
countRequest.input("resourceId", resourceId);
|
|
@@ -245,16 +1288,22 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
245
1288
|
threads: [],
|
|
246
1289
|
total: 0,
|
|
247
1290
|
page,
|
|
248
|
-
perPage,
|
|
1291
|
+
perPage: perPageForResponse,
|
|
249
1292
|
hasMore: false
|
|
250
1293
|
};
|
|
251
1294
|
}
|
|
252
|
-
const orderByField =
|
|
253
|
-
const
|
|
1295
|
+
const orderByField = field === "createdAt" ? "[createdAt]" : "[updatedAt]";
|
|
1296
|
+
const dir = (direction || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
|
|
1297
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
1298
|
+
const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir} OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
254
1299
|
const dataRequest = this.pool.request();
|
|
255
1300
|
dataRequest.input("resourceId", resourceId);
|
|
256
|
-
dataRequest.input("
|
|
257
|
-
|
|
1301
|
+
dataRequest.input("offset", offset);
|
|
1302
|
+
if (limitValue > 2147483647) {
|
|
1303
|
+
dataRequest.input("perPage", sql.BigInt, limitValue);
|
|
1304
|
+
} else {
|
|
1305
|
+
dataRequest.input("perPage", limitValue);
|
|
1306
|
+
}
|
|
258
1307
|
const rowsResult = await dataRequest.query(dataQuery);
|
|
259
1308
|
const rows = rowsResult.recordset || [];
|
|
260
1309
|
const threads = rows.map((thread) => ({
|
|
@@ -267,13 +1316,13 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
267
1316
|
threads,
|
|
268
1317
|
total,
|
|
269
1318
|
page,
|
|
270
|
-
perPage,
|
|
271
|
-
hasMore:
|
|
1319
|
+
perPage: perPageForResponse,
|
|
1320
|
+
hasMore: perPageInput === false ? false : offset + perPage < total
|
|
272
1321
|
};
|
|
273
1322
|
} catch (error) {
|
|
274
1323
|
const mastraError = new MastraError(
|
|
275
1324
|
{
|
|
276
|
-
id: "
|
|
1325
|
+
id: createStorageErrorId("MSSQL", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
|
|
277
1326
|
domain: ErrorDomain.STORAGE,
|
|
278
1327
|
category: ErrorCategory.THIRD_PARTY,
|
|
279
1328
|
details: {
|
|
@@ -285,12 +1334,18 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
285
1334
|
);
|
|
286
1335
|
this.logger?.error?.(mastraError.toString());
|
|
287
1336
|
this.logger?.trackException?.(mastraError);
|
|
288
|
-
return {
|
|
1337
|
+
return {
|
|
1338
|
+
threads: [],
|
|
1339
|
+
total: 0,
|
|
1340
|
+
page,
|
|
1341
|
+
perPage: perPageForResponse,
|
|
1342
|
+
hasMore: false
|
|
1343
|
+
};
|
|
289
1344
|
}
|
|
290
1345
|
}
|
|
291
1346
|
async saveThread({ thread }) {
|
|
292
1347
|
try {
|
|
293
|
-
const table =
|
|
1348
|
+
const table = getTableName2({ indexName: TABLE_THREADS, schemaName: getSchemaName2(this.schema) });
|
|
294
1349
|
const mergeSql = `MERGE INTO ${table} WITH (HOLDLOCK) AS target
|
|
295
1350
|
USING (SELECT @id AS id) AS source
|
|
296
1351
|
ON (target.id = source.id)
|
|
@@ -307,15 +1362,20 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
307
1362
|
req.input("id", thread.id);
|
|
308
1363
|
req.input("resourceId", thread.resourceId);
|
|
309
1364
|
req.input("title", thread.title);
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
1365
|
+
const metadata = thread.metadata ? JSON.stringify(thread.metadata) : null;
|
|
1366
|
+
if (metadata === null) {
|
|
1367
|
+
req.input("metadata", sql.NVarChar, null);
|
|
1368
|
+
} else {
|
|
1369
|
+
req.input("metadata", metadata);
|
|
1370
|
+
}
|
|
1371
|
+
req.input("createdAt", sql.DateTime2, thread.createdAt);
|
|
1372
|
+
req.input("updatedAt", sql.DateTime2, thread.updatedAt);
|
|
313
1373
|
await req.query(mergeSql);
|
|
314
1374
|
return thread;
|
|
315
1375
|
} catch (error) {
|
|
316
1376
|
throw new MastraError(
|
|
317
1377
|
{
|
|
318
|
-
id: "
|
|
1378
|
+
id: createStorageErrorId("MSSQL", "SAVE_THREAD", "FAILED"),
|
|
319
1379
|
domain: ErrorDomain.STORAGE,
|
|
320
1380
|
category: ErrorCategory.THIRD_PARTY,
|
|
321
1381
|
details: {
|
|
@@ -326,30 +1386,6 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
326
1386
|
);
|
|
327
1387
|
}
|
|
328
1388
|
}
|
|
329
|
-
/**
|
|
330
|
-
* @deprecated use getThreadsByResourceIdPaginated instead
|
|
331
|
-
*/
|
|
332
|
-
async getThreadsByResourceId(args) {
|
|
333
|
-
const { resourceId, orderBy = "createdAt", sortDirection = "DESC" } = args;
|
|
334
|
-
try {
|
|
335
|
-
const baseQuery = `FROM ${getTableName({ indexName: TABLE_THREADS, schemaName: getSchemaName(this.schema) })} WHERE [resourceId] = @resourceId`;
|
|
336
|
-
const orderByField = orderBy === "createdAt" ? "[createdAt]" : "[updatedAt]";
|
|
337
|
-
const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${sortDirection}`;
|
|
338
|
-
const request = this.pool.request();
|
|
339
|
-
request.input("resourceId", resourceId);
|
|
340
|
-
const resultSet = await request.query(dataQuery);
|
|
341
|
-
const rows = resultSet.recordset || [];
|
|
342
|
-
return rows.map((thread) => ({
|
|
343
|
-
...thread,
|
|
344
|
-
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
345
|
-
createdAt: thread.createdAt,
|
|
346
|
-
updatedAt: thread.updatedAt
|
|
347
|
-
}));
|
|
348
|
-
} catch (error) {
|
|
349
|
-
this.logger?.error?.(`Error getting threads for resource ${resourceId}:`, error);
|
|
350
|
-
return [];
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
1389
|
/**
|
|
354
1390
|
* Updates a thread's title and metadata, merging with existing metadata. Returns the updated thread.
|
|
355
1391
|
*/
|
|
@@ -361,7 +1397,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
361
1397
|
const existingThread = await this.getThreadById({ threadId: id });
|
|
362
1398
|
if (!existingThread) {
|
|
363
1399
|
throw new MastraError({
|
|
364
|
-
id: "
|
|
1400
|
+
id: createStorageErrorId("MSSQL", "UPDATE_THREAD", "NOT_FOUND"),
|
|
365
1401
|
domain: ErrorDomain.STORAGE,
|
|
366
1402
|
category: ErrorCategory.USER,
|
|
367
1403
|
text: `Thread ${id} not found`,
|
|
@@ -376,8 +1412,8 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
376
1412
|
...metadata
|
|
377
1413
|
};
|
|
378
1414
|
try {
|
|
379
|
-
const table =
|
|
380
|
-
const
|
|
1415
|
+
const table = getTableName2({ indexName: TABLE_THREADS, schemaName: getSchemaName2(this.schema) });
|
|
1416
|
+
const sql5 = `UPDATE ${table}
|
|
381
1417
|
SET title = @title,
|
|
382
1418
|
metadata = @metadata,
|
|
383
1419
|
[updatedAt] = @updatedAt
|
|
@@ -388,7 +1424,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
388
1424
|
req.input("title", title);
|
|
389
1425
|
req.input("metadata", JSON.stringify(mergedMetadata));
|
|
390
1426
|
req.input("updatedAt", /* @__PURE__ */ new Date());
|
|
391
|
-
const result = await req.query(
|
|
1427
|
+
const result = await req.query(sql5);
|
|
392
1428
|
let thread = result.recordset && result.recordset[0];
|
|
393
1429
|
if (thread && "seq_id" in thread) {
|
|
394
1430
|
const { seq_id, ...rest } = thread;
|
|
@@ -396,7 +1432,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
396
1432
|
}
|
|
397
1433
|
if (!thread) {
|
|
398
1434
|
throw new MastraError({
|
|
399
|
-
id: "
|
|
1435
|
+
id: createStorageErrorId("MSSQL", "UPDATE_THREAD", "NOT_FOUND"),
|
|
400
1436
|
domain: ErrorDomain.STORAGE,
|
|
401
1437
|
category: ErrorCategory.USER,
|
|
402
1438
|
text: `Thread ${id} not found after update`,
|
|
@@ -415,7 +1451,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
415
1451
|
} catch (error) {
|
|
416
1452
|
throw new MastraError(
|
|
417
1453
|
{
|
|
418
|
-
id: "
|
|
1454
|
+
id: createStorageErrorId("MSSQL", "UPDATE_THREAD", "FAILED"),
|
|
419
1455
|
domain: ErrorDomain.STORAGE,
|
|
420
1456
|
category: ErrorCategory.THIRD_PARTY,
|
|
421
1457
|
details: {
|
|
@@ -428,8 +1464,8 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
428
1464
|
}
|
|
429
1465
|
}
|
|
430
1466
|
async deleteThread({ threadId }) {
|
|
431
|
-
const messagesTable =
|
|
432
|
-
const threadsTable =
|
|
1467
|
+
const messagesTable = getTableName2({ indexName: TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) });
|
|
1468
|
+
const threadsTable = getTableName2({ indexName: TABLE_THREADS, schemaName: getSchemaName2(this.schema) });
|
|
433
1469
|
const deleteMessagesSql = `DELETE FROM ${messagesTable} WHERE [thread_id] = @threadId`;
|
|
434
1470
|
const deleteThreadSql = `DELETE FROM ${threadsTable} WHERE id = @threadId`;
|
|
435
1471
|
const tx = this.pool.transaction();
|
|
@@ -445,7 +1481,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
445
1481
|
});
|
|
446
1482
|
throw new MastraError(
|
|
447
1483
|
{
|
|
448
|
-
id: "
|
|
1484
|
+
id: createStorageErrorId("MSSQL", "DELETE_THREAD", "FAILED"),
|
|
449
1485
|
domain: ErrorDomain.STORAGE,
|
|
450
1486
|
category: ErrorCategory.THIRD_PARTY,
|
|
451
1487
|
details: {
|
|
@@ -456,25 +1492,18 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
456
1492
|
);
|
|
457
1493
|
}
|
|
458
1494
|
}
|
|
459
|
-
async _getIncludedMessages({
|
|
460
|
-
|
|
461
|
-
selectBy,
|
|
462
|
-
orderByStatement
|
|
463
|
-
}) {
|
|
464
|
-
if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
|
|
465
|
-
const include = selectBy?.include;
|
|
466
|
-
if (!include) return null;
|
|
1495
|
+
async _getIncludedMessages({ include }) {
|
|
1496
|
+
if (!include || include.length === 0) return null;
|
|
467
1497
|
const unionQueries = [];
|
|
468
1498
|
const paramValues = [];
|
|
469
1499
|
let paramIdx = 1;
|
|
470
1500
|
const paramNames = [];
|
|
1501
|
+
const tableName = getTableName2({ indexName: TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) });
|
|
471
1502
|
for (const inc of include) {
|
|
472
1503
|
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
473
|
-
const
|
|
474
|
-
const
|
|
475
|
-
const
|
|
476
|
-
const pPrev = `@p${paramIdx + 2}`;
|
|
477
|
-
const pNext = `@p${paramIdx + 3}`;
|
|
1504
|
+
const pId = `@p${paramIdx}`;
|
|
1505
|
+
const pPrev = `@p${paramIdx + 1}`;
|
|
1506
|
+
const pNext = `@p${paramIdx + 2}`;
|
|
478
1507
|
unionQueries.push(
|
|
479
1508
|
`
|
|
480
1509
|
SELECT
|
|
@@ -487,30 +1516,32 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
487
1516
|
m.[resourceId],
|
|
488
1517
|
m.seq_id
|
|
489
1518
|
FROM (
|
|
490
|
-
SELECT *, ROW_NUMBER() OVER (
|
|
491
|
-
FROM ${
|
|
492
|
-
WHERE [thread_id] = ${
|
|
1519
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
|
|
1520
|
+
FROM ${tableName}
|
|
1521
|
+
WHERE [thread_id] = (SELECT thread_id FROM ${tableName} WHERE id = ${pId})
|
|
493
1522
|
) AS m
|
|
494
1523
|
WHERE m.id = ${pId}
|
|
495
1524
|
OR EXISTS (
|
|
496
1525
|
SELECT 1
|
|
497
1526
|
FROM (
|
|
498
|
-
SELECT *, ROW_NUMBER() OVER (
|
|
499
|
-
FROM ${
|
|
500
|
-
WHERE [thread_id] = ${
|
|
1527
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
|
|
1528
|
+
FROM ${tableName}
|
|
1529
|
+
WHERE [thread_id] = (SELECT thread_id FROM ${tableName} WHERE id = ${pId})
|
|
501
1530
|
) AS target
|
|
502
1531
|
WHERE target.id = ${pId}
|
|
503
1532
|
AND (
|
|
504
|
-
|
|
1533
|
+
-- Get previous messages (messages that come BEFORE the target)
|
|
1534
|
+
(m.row_num < target.row_num AND m.row_num >= target.row_num - ${pPrev})
|
|
505
1535
|
OR
|
|
506
|
-
|
|
1536
|
+
-- Get next messages (messages that come AFTER the target)
|
|
1537
|
+
(m.row_num > target.row_num AND m.row_num <= target.row_num + ${pNext})
|
|
507
1538
|
)
|
|
508
1539
|
)
|
|
509
1540
|
`
|
|
510
1541
|
);
|
|
511
|
-
paramValues.push(
|
|
512
|
-
paramNames.push(`p${paramIdx}`, `p${paramIdx + 1}`, `p${paramIdx + 2}
|
|
513
|
-
paramIdx +=
|
|
1542
|
+
paramValues.push(id, withPreviousMessages, withNextMessages);
|
|
1543
|
+
paramNames.push(`p${paramIdx}`, `p${paramIdx + 1}`, `p${paramIdx + 2}`);
|
|
1544
|
+
paramIdx += 3;
|
|
514
1545
|
}
|
|
515
1546
|
const finalQuery = `
|
|
516
1547
|
SELECT * FROM (
|
|
@@ -532,34 +1563,16 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
532
1563
|
});
|
|
533
1564
|
return dedupedRows;
|
|
534
1565
|
}
|
|
535
|
-
async
|
|
536
|
-
|
|
1566
|
+
async listMessagesById({ messageIds }) {
|
|
1567
|
+
if (messageIds.length === 0) return { messages: [] };
|
|
537
1568
|
const selectStatement = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId`;
|
|
538
1569
|
const orderByStatement = `ORDER BY [seq_id] DESC`;
|
|
539
|
-
const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
540
1570
|
try {
|
|
541
|
-
if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
|
|
542
1571
|
let rows = [];
|
|
543
|
-
|
|
544
|
-
if (include?.length) {
|
|
545
|
-
const includeMessages = await this._getIncludedMessages({ threadId, selectBy, orderByStatement });
|
|
546
|
-
if (includeMessages) {
|
|
547
|
-
rows.push(...includeMessages);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
const excludeIds = rows.map((m) => m.id).filter(Boolean);
|
|
551
|
-
let query = `${selectStatement} FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })} WHERE [thread_id] = @threadId`;
|
|
1572
|
+
let query = `${selectStatement} FROM ${getTableName2({ indexName: TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) })} WHERE [id] IN (${messageIds.map((_, i) => `@id${i}`).join(", ")})`;
|
|
552
1573
|
const request = this.pool.request();
|
|
553
|
-
request.input(
|
|
554
|
-
|
|
555
|
-
const excludeParams = excludeIds.map((_, idx) => `@id${idx}`);
|
|
556
|
-
query += ` AND id NOT IN (${excludeParams.join(", ")})`;
|
|
557
|
-
excludeIds.forEach((id, idx) => {
|
|
558
|
-
request.input(`id${idx}`, id);
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
query += ` ${orderByStatement} OFFSET 0 ROWS FETCH NEXT @limit ROWS ONLY`;
|
|
562
|
-
request.input("limit", limit);
|
|
1574
|
+
messageIds.forEach((id, i) => request.input(`id${i}`, id));
|
|
1575
|
+
query += ` ${orderByStatement}`;
|
|
563
1576
|
const result = await request.query(query);
|
|
564
1577
|
const remainingRows = result.recordset || [];
|
|
565
1578
|
rows.push(...remainingRows);
|
|
@@ -567,157 +1580,177 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
567
1580
|
const timeDiff = a.seq_id - b.seq_id;
|
|
568
1581
|
return timeDiff;
|
|
569
1582
|
});
|
|
570
|
-
|
|
571
|
-
|
|
1583
|
+
const messagesWithParsedContent = rows.map((row) => {
|
|
1584
|
+
if (typeof row.content === "string") {
|
|
1585
|
+
try {
|
|
1586
|
+
return { ...row, content: JSON.parse(row.content) };
|
|
1587
|
+
} catch {
|
|
1588
|
+
return row;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
return row;
|
|
1592
|
+
});
|
|
1593
|
+
const cleanMessages = messagesWithParsedContent.map(({ seq_id, ...rest }) => rest);
|
|
1594
|
+
const list = new MessageList().add(cleanMessages, "memory");
|
|
1595
|
+
return { messages: list.get.all.db() };
|
|
572
1596
|
} catch (error) {
|
|
573
1597
|
const mastraError = new MastraError(
|
|
574
1598
|
{
|
|
575
|
-
id: "
|
|
1599
|
+
id: createStorageErrorId("MSSQL", "LIST_MESSAGES_BY_ID", "FAILED"),
|
|
576
1600
|
domain: ErrorDomain.STORAGE,
|
|
577
1601
|
category: ErrorCategory.THIRD_PARTY,
|
|
578
1602
|
details: {
|
|
579
|
-
|
|
580
|
-
resourceId: resourceId ?? ""
|
|
1603
|
+
messageIds: JSON.stringify(messageIds)
|
|
581
1604
|
}
|
|
582
1605
|
},
|
|
583
1606
|
error
|
|
584
1607
|
);
|
|
585
1608
|
this.logger?.error?.(mastraError.toString());
|
|
586
|
-
this.logger?.trackException(mastraError);
|
|
587
|
-
return [];
|
|
1609
|
+
this.logger?.trackException?.(mastraError);
|
|
1610
|
+
return { messages: [] };
|
|
588
1611
|
}
|
|
589
1612
|
}
|
|
590
|
-
async
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
const selectStatement = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId`;
|
|
596
|
-
const orderByStatement = `ORDER BY [seq_id] DESC`;
|
|
597
|
-
try {
|
|
598
|
-
let rows = [];
|
|
599
|
-
let query = `${selectStatement} FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })} WHERE [id] IN (${messageIds.map((_, i) => `@id${i}`).join(", ")})`;
|
|
600
|
-
const request = this.pool.request();
|
|
601
|
-
messageIds.forEach((id, i) => request.input(`id${i}`, id));
|
|
602
|
-
query += ` ${orderByStatement}`;
|
|
603
|
-
const result = await request.query(query);
|
|
604
|
-
const remainingRows = result.recordset || [];
|
|
605
|
-
rows.push(...remainingRows);
|
|
606
|
-
rows.sort((a, b) => {
|
|
607
|
-
const timeDiff = a.seq_id - b.seq_id;
|
|
608
|
-
return timeDiff;
|
|
609
|
-
});
|
|
610
|
-
rows = rows.map(({ seq_id, ...rest }) => rest);
|
|
611
|
-
if (format === `v1`) return this._parseAndFormatMessages(rows, format);
|
|
612
|
-
return this._parseAndFormatMessages(rows, `v2`);
|
|
613
|
-
} catch (error) {
|
|
614
|
-
const mastraError = new MastraError(
|
|
1613
|
+
async listMessages(args) {
|
|
1614
|
+
const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
|
|
1615
|
+
const threadIds = Array.isArray(threadId) ? threadId : [threadId];
|
|
1616
|
+
if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
|
|
1617
|
+
throw new MastraError(
|
|
615
1618
|
{
|
|
616
|
-
id: "
|
|
1619
|
+
id: createStorageErrorId("MSSQL", "LIST_MESSAGES", "INVALID_THREAD_ID"),
|
|
617
1620
|
domain: ErrorDomain.STORAGE,
|
|
618
1621
|
category: ErrorCategory.THIRD_PARTY,
|
|
619
|
-
details: {
|
|
620
|
-
messageIds: JSON.stringify(messageIds)
|
|
621
|
-
}
|
|
1622
|
+
details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
|
|
622
1623
|
},
|
|
623
|
-
|
|
1624
|
+
new Error("threadId must be a non-empty string or array of non-empty strings")
|
|
624
1625
|
);
|
|
625
|
-
this.logger?.error?.(mastraError.toString());
|
|
626
|
-
this.logger?.trackException(mastraError);
|
|
627
|
-
return [];
|
|
628
1626
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
1627
|
+
if (page < 0) {
|
|
1628
|
+
throw new MastraError({
|
|
1629
|
+
id: createStorageErrorId("MSSQL", "LIST_MESSAGES", "INVALID_PAGE"),
|
|
1630
|
+
domain: ErrorDomain.STORAGE,
|
|
1631
|
+
category: ErrorCategory.USER,
|
|
1632
|
+
text: "Page number must be non-negative",
|
|
1633
|
+
details: {
|
|
1634
|
+
threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
|
|
1635
|
+
page
|
|
1636
|
+
}
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
const perPage = normalizePerPage(perPageInput, 40);
|
|
1640
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
633
1641
|
try {
|
|
634
|
-
|
|
635
|
-
const
|
|
636
|
-
const
|
|
637
|
-
const
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
const
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
request
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
request.input("fromDate", fromDate.toISOString());
|
|
652
|
-
}
|
|
653
|
-
if (toDate instanceof Date && !isNaN(toDate.getTime())) {
|
|
654
|
-
conditions.push("[createdAt] <= @toDate");
|
|
655
|
-
request.input("toDate", toDate.toISOString());
|
|
656
|
-
}
|
|
657
|
-
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
658
|
-
const countQuery = `SELECT COUNT(*) as total FROM ${getTableName({ indexName: TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })} ${whereClause}`;
|
|
659
|
-
const countResult = await request.query(countQuery);
|
|
1642
|
+
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
1643
|
+
const orderByStatement = `ORDER BY [${field}] ${direction}, [seq_id] ${direction}`;
|
|
1644
|
+
const tableName = getTableName2({ indexName: TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) });
|
|
1645
|
+
const baseQuery = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId FROM ${tableName}`;
|
|
1646
|
+
const filters = {
|
|
1647
|
+
thread_id: threadIds.length === 1 ? threadIds[0] : { $in: threadIds },
|
|
1648
|
+
...resourceId ? { resourceId } : {},
|
|
1649
|
+
...buildDateRangeFilter(filter?.dateRange, "createdAt")
|
|
1650
|
+
};
|
|
1651
|
+
const { sql: actualWhereClause = "", params: whereParams } = prepareWhereClause(
|
|
1652
|
+
filters);
|
|
1653
|
+
const bindWhereParams = (req) => {
|
|
1654
|
+
Object.entries(whereParams).forEach(([paramName, paramValue]) => req.input(paramName, paramValue));
|
|
1655
|
+
};
|
|
1656
|
+
const countRequest = this.pool.request();
|
|
1657
|
+
bindWhereParams(countRequest);
|
|
1658
|
+
const countResult = await countRequest.query(`SELECT COUNT(*) as total FROM ${tableName}${actualWhereClause}`);
|
|
660
1659
|
const total = parseInt(countResult.recordset[0]?.total, 10) || 0;
|
|
661
|
-
|
|
662
|
-
const
|
|
1660
|
+
const fetchBaseMessages = async () => {
|
|
1661
|
+
const request = this.pool.request();
|
|
1662
|
+
bindWhereParams(request);
|
|
1663
|
+
if (perPageInput === false) {
|
|
1664
|
+
const result2 = await request.query(`${baseQuery}${actualWhereClause} ${orderByStatement}`);
|
|
1665
|
+
return result2.recordset || [];
|
|
1666
|
+
}
|
|
1667
|
+
request.input("offset", offset);
|
|
1668
|
+
request.input("limit", perPage > 2147483647 ? sql.BigInt : sql.Int, perPage);
|
|
1669
|
+
const result = await request.query(
|
|
1670
|
+
`${baseQuery}${actualWhereClause} ${orderByStatement} OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
|
|
1671
|
+
);
|
|
1672
|
+
return result.recordset || [];
|
|
1673
|
+
};
|
|
1674
|
+
const baseRows = perPage === 0 ? [] : await fetchBaseMessages();
|
|
1675
|
+
const messages = [...baseRows];
|
|
1676
|
+
const seqById = /* @__PURE__ */ new Map();
|
|
1677
|
+
messages.forEach((msg) => {
|
|
1678
|
+
if (typeof msg.seq_id === "number") seqById.set(msg.id, msg.seq_id);
|
|
1679
|
+
});
|
|
1680
|
+
if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
|
|
663
1681
|
return {
|
|
664
|
-
messages:
|
|
665
|
-
total:
|
|
1682
|
+
messages: [],
|
|
1683
|
+
total: 0,
|
|
666
1684
|
page,
|
|
667
|
-
perPage,
|
|
1685
|
+
perPage: perPageForResponse,
|
|
668
1686
|
hasMore: false
|
|
669
1687
|
};
|
|
670
1688
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
1689
|
+
if (include?.length) {
|
|
1690
|
+
const messageIds = new Set(messages.map((m) => m.id));
|
|
1691
|
+
const includeMessages = await this._getIncludedMessages({ include });
|
|
1692
|
+
includeMessages?.forEach((msg) => {
|
|
1693
|
+
if (!messageIds.has(msg.id)) {
|
|
1694
|
+
messages.push(msg);
|
|
1695
|
+
messageIds.add(msg.id);
|
|
1696
|
+
if (typeof msg.seq_id === "number") seqById.set(msg.id, msg.seq_id);
|
|
1697
|
+
}
|
|
1698
|
+
});
|
|
1699
|
+
}
|
|
1700
|
+
const parsed = this._parseAndFormatMessages(messages, "v2");
|
|
1701
|
+
const mult = direction === "ASC" ? 1 : -1;
|
|
1702
|
+
const finalMessages = parsed.sort((a, b) => {
|
|
1703
|
+
const aVal = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
|
|
1704
|
+
const bVal = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
|
|
1705
|
+
if (aVal == null || bVal == null) {
|
|
1706
|
+
return aVal == null && bVal == null ? a.id.localeCompare(b.id) : aVal == null ? 1 : -1;
|
|
1707
|
+
}
|
|
1708
|
+
const diff = (typeof aVal === "number" && typeof bVal === "number" ? aVal - bVal : String(aVal).localeCompare(String(bVal))) * mult;
|
|
1709
|
+
if (diff !== 0) return diff;
|
|
1710
|
+
const seqA = seqById.get(a.id);
|
|
1711
|
+
const seqB = seqById.get(b.id);
|
|
1712
|
+
return seqA != null && seqB != null ? (seqA - seqB) * mult : a.id.localeCompare(b.id);
|
|
1713
|
+
});
|
|
1714
|
+
const threadIdSet = new Set(threadIds);
|
|
1715
|
+
const returnedThreadMessageCount = finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).length;
|
|
1716
|
+
const hasMore = perPageInput !== false && returnedThreadMessageCount < total && offset + perPage < total;
|
|
686
1717
|
return {
|
|
687
|
-
messages:
|
|
688
|
-
total
|
|
1718
|
+
messages: finalMessages,
|
|
1719
|
+
total,
|
|
689
1720
|
page,
|
|
690
|
-
perPage,
|
|
691
|
-
hasMore
|
|
1721
|
+
perPage: perPageForResponse,
|
|
1722
|
+
hasMore
|
|
692
1723
|
};
|
|
693
1724
|
} catch (error) {
|
|
694
1725
|
const mastraError = new MastraError(
|
|
695
1726
|
{
|
|
696
|
-
id: "
|
|
1727
|
+
id: createStorageErrorId("MSSQL", "LIST_MESSAGES", "FAILED"),
|
|
697
1728
|
domain: ErrorDomain.STORAGE,
|
|
698
1729
|
category: ErrorCategory.THIRD_PARTY,
|
|
699
1730
|
details: {
|
|
700
|
-
threadId,
|
|
701
|
-
resourceId: resourceId ?? ""
|
|
702
|
-
page
|
|
1731
|
+
threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
|
|
1732
|
+
resourceId: resourceId ?? ""
|
|
703
1733
|
}
|
|
704
1734
|
},
|
|
705
1735
|
error
|
|
706
1736
|
);
|
|
707
1737
|
this.logger?.error?.(mastraError.toString());
|
|
708
|
-
this.logger?.trackException(mastraError);
|
|
709
|
-
return {
|
|
1738
|
+
this.logger?.trackException?.(mastraError);
|
|
1739
|
+
return {
|
|
1740
|
+
messages: [],
|
|
1741
|
+
total: 0,
|
|
1742
|
+
page,
|
|
1743
|
+
perPage: perPageForResponse,
|
|
1744
|
+
hasMore: false
|
|
1745
|
+
};
|
|
710
1746
|
}
|
|
711
1747
|
}
|
|
712
|
-
async saveMessages({
|
|
713
|
-
messages
|
|
714
|
-
format
|
|
715
|
-
}) {
|
|
716
|
-
if (messages.length === 0) return messages;
|
|
1748
|
+
async saveMessages({ messages }) {
|
|
1749
|
+
if (messages.length === 0) return { messages: [] };
|
|
717
1750
|
const threadId = messages[0]?.threadId;
|
|
718
1751
|
if (!threadId) {
|
|
719
1752
|
throw new MastraError({
|
|
720
|
-
id: "
|
|
1753
|
+
id: createStorageErrorId("MSSQL", "SAVE_MESSAGES", "INVALID_THREAD_ID"),
|
|
721
1754
|
domain: ErrorDomain.STORAGE,
|
|
722
1755
|
category: ErrorCategory.THIRD_PARTY,
|
|
723
1756
|
text: `Thread ID is required`
|
|
@@ -726,15 +1759,15 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
726
1759
|
const thread = await this.getThreadById({ threadId });
|
|
727
1760
|
if (!thread) {
|
|
728
1761
|
throw new MastraError({
|
|
729
|
-
id: "
|
|
1762
|
+
id: createStorageErrorId("MSSQL", "SAVE_MESSAGES", "THREAD_NOT_FOUND"),
|
|
730
1763
|
domain: ErrorDomain.STORAGE,
|
|
731
1764
|
category: ErrorCategory.THIRD_PARTY,
|
|
732
1765
|
text: `Thread ${threadId} not found`,
|
|
733
1766
|
details: { threadId }
|
|
734
1767
|
});
|
|
735
1768
|
}
|
|
736
|
-
const tableMessages =
|
|
737
|
-
const tableThreads =
|
|
1769
|
+
const tableMessages = getTableName2({ indexName: TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) });
|
|
1770
|
+
const tableThreads = getTableName2({ indexName: TABLE_THREADS, schemaName: getSchemaName2(this.schema) });
|
|
738
1771
|
try {
|
|
739
1772
|
const transaction = this.pool.transaction();
|
|
740
1773
|
await transaction.begin();
|
|
@@ -757,7 +1790,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
757
1790
|
"content",
|
|
758
1791
|
typeof message.content === "string" ? message.content : JSON.stringify(message.content)
|
|
759
1792
|
);
|
|
760
|
-
request.input("createdAt",
|
|
1793
|
+
request.input("createdAt", sql.DateTime2, message.createdAt);
|
|
761
1794
|
request.input("role", message.role);
|
|
762
1795
|
request.input("type", message.type || "v2");
|
|
763
1796
|
request.input("resourceId", message.resourceId);
|
|
@@ -776,7 +1809,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
776
1809
|
await request.query(mergeSql);
|
|
777
1810
|
}
|
|
778
1811
|
const threadReq = transaction.request();
|
|
779
|
-
threadReq.input("updatedAt",
|
|
1812
|
+
threadReq.input("updatedAt", sql.DateTime2, /* @__PURE__ */ new Date());
|
|
780
1813
|
threadReq.input("id", threadId);
|
|
781
1814
|
await threadReq.query(`UPDATE ${tableThreads} SET [updatedAt] = @updatedAt WHERE id = @id`);
|
|
782
1815
|
await transaction.commit();
|
|
@@ -795,12 +1828,11 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
795
1828
|
return message;
|
|
796
1829
|
});
|
|
797
1830
|
const list = new MessageList().add(messagesWithParsedContent, "memory");
|
|
798
|
-
|
|
799
|
-
return list.get.all.v1();
|
|
1831
|
+
return { messages: list.get.all.db() };
|
|
800
1832
|
} catch (error) {
|
|
801
1833
|
throw new MastraError(
|
|
802
1834
|
{
|
|
803
|
-
id: "
|
|
1835
|
+
id: createStorageErrorId("MSSQL", "SAVE_MESSAGES", "FAILED"),
|
|
804
1836
|
domain: ErrorDomain.STORAGE,
|
|
805
1837
|
category: ErrorCategory.THIRD_PARTY,
|
|
806
1838
|
details: { threadId }
|
|
@@ -817,7 +1849,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
817
1849
|
}
|
|
818
1850
|
const messageIds = messages.map((m) => m.id);
|
|
819
1851
|
const idParams = messageIds.map((_, i) => `@id${i}`).join(", ");
|
|
820
|
-
let selectQuery = `SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId FROM ${
|
|
1852
|
+
let selectQuery = `SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId FROM ${getTableName2({ indexName: TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) })}`;
|
|
821
1853
|
if (idParams.length > 0) {
|
|
822
1854
|
selectQuery += ` WHERE id IN (${idParams})`;
|
|
823
1855
|
} else {
|
|
@@ -874,7 +1906,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
874
1906
|
}
|
|
875
1907
|
}
|
|
876
1908
|
if (setClauses.length > 0) {
|
|
877
|
-
const updateSql = `UPDATE ${
|
|
1909
|
+
const updateSql = `UPDATE ${getTableName2({ indexName: TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) })} SET ${setClauses.join(", ")} WHERE id = @id`;
|
|
878
1910
|
await req.query(updateSql);
|
|
879
1911
|
}
|
|
880
1912
|
}
|
|
@@ -883,7 +1915,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
883
1915
|
const threadReq = transaction.request();
|
|
884
1916
|
Array.from(threadIdsToUpdate).forEach((tid, i) => threadReq.input(`tid${i}`, tid));
|
|
885
1917
|
threadReq.input("updatedAt", (/* @__PURE__ */ new Date()).toISOString());
|
|
886
|
-
const threadSql = `UPDATE ${
|
|
1918
|
+
const threadSql = `UPDATE ${getTableName2({ indexName: TABLE_THREADS, schemaName: getSchemaName2(this.schema) })} SET updatedAt = @updatedAt WHERE id IN (${threadIdParams})`;
|
|
887
1919
|
await threadReq.query(threadSql);
|
|
888
1920
|
}
|
|
889
1921
|
await transaction.commit();
|
|
@@ -891,7 +1923,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
891
1923
|
await transaction.rollback();
|
|
892
1924
|
throw new MastraError(
|
|
893
1925
|
{
|
|
894
|
-
id: "
|
|
1926
|
+
id: createStorageErrorId("MSSQL", "UPDATE_MESSAGES", "FAILED"),
|
|
895
1927
|
domain: ErrorDomain.STORAGE,
|
|
896
1928
|
category: ErrorCategory.THIRD_PARTY
|
|
897
1929
|
},
|
|
@@ -916,8 +1948,8 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
916
1948
|
return;
|
|
917
1949
|
}
|
|
918
1950
|
try {
|
|
919
|
-
const messageTableName =
|
|
920
|
-
const threadTableName =
|
|
1951
|
+
const messageTableName = getTableName2({ indexName: TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) });
|
|
1952
|
+
const threadTableName = getTableName2({ indexName: TABLE_THREADS, schemaName: getSchemaName2(this.schema) });
|
|
921
1953
|
const placeholders = messageIds.map((_, idx) => `@p${idx + 1}`).join(",");
|
|
922
1954
|
const request = this.pool.request();
|
|
923
1955
|
messageIds.forEach((id, idx) => {
|
|
@@ -953,7 +1985,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
953
1985
|
} catch (error) {
|
|
954
1986
|
throw new MastraError(
|
|
955
1987
|
{
|
|
956
|
-
id: "
|
|
1988
|
+
id: createStorageErrorId("MSSQL", "DELETE_MESSAGES", "FAILED"),
|
|
957
1989
|
domain: ErrorDomain.STORAGE,
|
|
958
1990
|
category: ErrorCategory.THIRD_PARTY,
|
|
959
1991
|
details: { messageIds: messageIds.join(", ") }
|
|
@@ -963,7 +1995,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
963
1995
|
}
|
|
964
1996
|
}
|
|
965
1997
|
async getResourceById({ resourceId }) {
|
|
966
|
-
const tableName =
|
|
1998
|
+
const tableName = getTableName2({ indexName: TABLE_RESOURCES, schemaName: getSchemaName2(this.schema) });
|
|
967
1999
|
try {
|
|
968
2000
|
const req = this.pool.request();
|
|
969
2001
|
req.input("resourceId", resourceId);
|
|
@@ -972,14 +2004,16 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
972
2004
|
return null;
|
|
973
2005
|
}
|
|
974
2006
|
return {
|
|
975
|
-
|
|
976
|
-
|
|
2007
|
+
id: result.id,
|
|
2008
|
+
createdAt: result.createdAt,
|
|
2009
|
+
updatedAt: result.updatedAt,
|
|
2010
|
+
workingMemory: result.workingMemory,
|
|
977
2011
|
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
|
|
978
2012
|
};
|
|
979
2013
|
} catch (error) {
|
|
980
2014
|
const mastraError = new MastraError(
|
|
981
2015
|
{
|
|
982
|
-
id: "
|
|
2016
|
+
id: createStorageErrorId("MSSQL", "GET_RESOURCE_BY_ID", "FAILED"),
|
|
983
2017
|
domain: ErrorDomain.STORAGE,
|
|
984
2018
|
category: ErrorCategory.THIRD_PARTY,
|
|
985
2019
|
details: { resourceId }
|
|
@@ -987,16 +2021,16 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
987
2021
|
error
|
|
988
2022
|
);
|
|
989
2023
|
this.logger?.error?.(mastraError.toString());
|
|
990
|
-
this.logger?.trackException(mastraError);
|
|
2024
|
+
this.logger?.trackException?.(mastraError);
|
|
991
2025
|
throw mastraError;
|
|
992
2026
|
}
|
|
993
2027
|
}
|
|
994
2028
|
async saveResource({ resource }) {
|
|
995
|
-
await this.
|
|
2029
|
+
await this.db.insert({
|
|
996
2030
|
tableName: TABLE_RESOURCES,
|
|
997
2031
|
record: {
|
|
998
2032
|
...resource,
|
|
999
|
-
metadata:
|
|
2033
|
+
metadata: resource.metadata
|
|
1000
2034
|
}
|
|
1001
2035
|
});
|
|
1002
2036
|
return resource;
|
|
@@ -1027,7 +2061,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
1027
2061
|
},
|
|
1028
2062
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1029
2063
|
};
|
|
1030
|
-
const tableName =
|
|
2064
|
+
const tableName = getTableName2({ indexName: TABLE_RESOURCES, schemaName: getSchemaName2(this.schema) });
|
|
1031
2065
|
const updates = [];
|
|
1032
2066
|
const req = this.pool.request();
|
|
1033
2067
|
if (workingMemory !== void 0) {
|
|
@@ -1046,7 +2080,7 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
1046
2080
|
} catch (error) {
|
|
1047
2081
|
const mastraError = new MastraError(
|
|
1048
2082
|
{
|
|
1049
|
-
id: "
|
|
2083
|
+
id: createStorageErrorId("MSSQL", "UPDATE_RESOURCE", "FAILED"),
|
|
1050
2084
|
domain: ErrorDomain.STORAGE,
|
|
1051
2085
|
category: ErrorCategory.THIRD_PARTY,
|
|
1052
2086
|
details: { resourceId }
|
|
@@ -1054,357 +2088,650 @@ var MemoryMSSQL = class extends MemoryStorage {
|
|
|
1054
2088
|
error
|
|
1055
2089
|
);
|
|
1056
2090
|
this.logger?.error?.(mastraError.toString());
|
|
1057
|
-
this.logger?.trackException(mastraError);
|
|
2091
|
+
this.logger?.trackException?.(mastraError);
|
|
1058
2092
|
throw mastraError;
|
|
1059
2093
|
}
|
|
1060
2094
|
}
|
|
1061
2095
|
};
|
|
1062
|
-
var
|
|
2096
|
+
var ObservabilityMSSQL = class _ObservabilityMSSQL extends ObservabilityStorage {
|
|
1063
2097
|
pool;
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
return "DATETIME2(7)";
|
|
1073
|
-
case "uuid":
|
|
1074
|
-
return "UNIQUEIDENTIFIER";
|
|
1075
|
-
case "jsonb":
|
|
1076
|
-
return "NVARCHAR(MAX)";
|
|
1077
|
-
case "integer":
|
|
1078
|
-
return "INT";
|
|
1079
|
-
case "bigint":
|
|
1080
|
-
return "BIGINT";
|
|
1081
|
-
case "float":
|
|
1082
|
-
return "FLOAT";
|
|
1083
|
-
default:
|
|
1084
|
-
throw new MastraError({
|
|
1085
|
-
id: "MASTRA_STORAGE_MSSQL_STORE_TYPE_NOT_SUPPORTED",
|
|
1086
|
-
domain: ErrorDomain.STORAGE,
|
|
1087
|
-
category: ErrorCategory.THIRD_PARTY
|
|
1088
|
-
});
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
constructor({ pool, schemaName }) {
|
|
2098
|
+
db;
|
|
2099
|
+
schema;
|
|
2100
|
+
needsConnect;
|
|
2101
|
+
skipDefaultIndexes;
|
|
2102
|
+
indexes;
|
|
2103
|
+
/** Tables managed by this domain */
|
|
2104
|
+
static MANAGED_TABLES = [TABLE_SPANS];
|
|
2105
|
+
constructor(config) {
|
|
1092
2106
|
super();
|
|
2107
|
+
const { pool, schemaName, skipDefaultIndexes, indexes, needsConnect } = resolveMssqlConfig(config);
|
|
1093
2108
|
this.pool = pool;
|
|
1094
|
-
this.
|
|
2109
|
+
this.schema = schemaName;
|
|
2110
|
+
this.db = new MssqlDB({ pool, schemaName, skipDefaultIndexes });
|
|
2111
|
+
this.needsConnect = needsConnect;
|
|
2112
|
+
this.skipDefaultIndexes = skipDefaultIndexes;
|
|
2113
|
+
this.indexes = indexes?.filter((idx) => _ObservabilityMSSQL.MANAGED_TABLES.includes(idx.table));
|
|
1095
2114
|
}
|
|
1096
|
-
async
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
`SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND (COLUMN_NAME = @column OR COLUMN_NAME = @columnLower)`
|
|
1105
|
-
);
|
|
1106
|
-
return result.recordset.length > 0;
|
|
2115
|
+
async init() {
|
|
2116
|
+
if (this.needsConnect) {
|
|
2117
|
+
await this.pool.connect();
|
|
2118
|
+
this.needsConnect = false;
|
|
2119
|
+
}
|
|
2120
|
+
await this.db.createTable({ tableName: TABLE_SPANS, schema: SPAN_SCHEMA });
|
|
2121
|
+
await this.createDefaultIndexes();
|
|
2122
|
+
await this.createCustomIndexes();
|
|
1107
2123
|
}
|
|
1108
|
-
|
|
1109
|
-
|
|
2124
|
+
/**
|
|
2125
|
+
* Returns default index definitions for the observability domain tables.
|
|
2126
|
+
*/
|
|
2127
|
+
getDefaultIndexDefinitions() {
|
|
2128
|
+
const schemaPrefix = this.schema ? `${this.schema}_` : "";
|
|
2129
|
+
return [
|
|
2130
|
+
{
|
|
2131
|
+
name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
|
|
2132
|
+
table: TABLE_SPANS,
|
|
2133
|
+
columns: ["traceId", "startedAt DESC"]
|
|
2134
|
+
},
|
|
2135
|
+
{
|
|
2136
|
+
name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
|
|
2137
|
+
table: TABLE_SPANS,
|
|
2138
|
+
columns: ["parentSpanId", "startedAt DESC"]
|
|
2139
|
+
},
|
|
2140
|
+
{
|
|
2141
|
+
name: `${schemaPrefix}mastra_ai_spans_name_idx`,
|
|
2142
|
+
table: TABLE_SPANS,
|
|
2143
|
+
columns: ["name"]
|
|
2144
|
+
},
|
|
2145
|
+
{
|
|
2146
|
+
name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
|
|
2147
|
+
table: TABLE_SPANS,
|
|
2148
|
+
columns: ["spanType", "startedAt DESC"]
|
|
2149
|
+
},
|
|
2150
|
+
// Root spans filtered index - every listTraces query filters parentSpanId IS NULL
|
|
2151
|
+
{
|
|
2152
|
+
name: `${schemaPrefix}mastra_ai_spans_root_spans_idx`,
|
|
2153
|
+
table: TABLE_SPANS,
|
|
2154
|
+
columns: ["startedAt DESC"],
|
|
2155
|
+
where: "[parentSpanId] IS NULL"
|
|
2156
|
+
},
|
|
2157
|
+
// Entity identification indexes - common filtering patterns
|
|
2158
|
+
{
|
|
2159
|
+
name: `${schemaPrefix}mastra_ai_spans_entitytype_entityid_idx`,
|
|
2160
|
+
table: TABLE_SPANS,
|
|
2161
|
+
columns: ["entityType", "entityId"]
|
|
2162
|
+
},
|
|
2163
|
+
{
|
|
2164
|
+
name: `${schemaPrefix}mastra_ai_spans_entitytype_entityname_idx`,
|
|
2165
|
+
table: TABLE_SPANS,
|
|
2166
|
+
columns: ["entityType", "entityName"]
|
|
2167
|
+
},
|
|
2168
|
+
// Multi-tenant filtering - organizationId + userId
|
|
2169
|
+
{
|
|
2170
|
+
name: `${schemaPrefix}mastra_ai_spans_orgid_userid_idx`,
|
|
2171
|
+
table: TABLE_SPANS,
|
|
2172
|
+
columns: ["organizationId", "userId"]
|
|
2173
|
+
}
|
|
2174
|
+
// Note: MSSQL doesn't support GIN indexes for JSONB/array containment queries
|
|
2175
|
+
// Metadata and tags filtering will use full table scans on NVARCHAR(MAX) columns
|
|
2176
|
+
];
|
|
2177
|
+
}
|
|
2178
|
+
/**
|
|
2179
|
+
* Creates default indexes for optimal query performance.
|
|
2180
|
+
*/
|
|
2181
|
+
async createDefaultIndexes() {
|
|
2182
|
+
if (this.skipDefaultIndexes) {
|
|
1110
2183
|
return;
|
|
1111
2184
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
SELECT 1 AS found FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @schemaName
|
|
1119
|
-
`);
|
|
1120
|
-
const schemaExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
1121
|
-
if (!schemaExists) {
|
|
1122
|
-
try {
|
|
1123
|
-
await this.pool.request().query(`CREATE SCHEMA [${this.schemaName}]`);
|
|
1124
|
-
this.logger?.info?.(`Schema "${this.schemaName}" created successfully`);
|
|
1125
|
-
} catch (error) {
|
|
1126
|
-
this.logger?.error?.(`Failed to create schema "${this.schemaName}"`, { error });
|
|
1127
|
-
throw new Error(
|
|
1128
|
-
`Unable to create schema "${this.schemaName}". This requires CREATE privilege on the database. Either create the schema manually or grant CREATE privilege to the user.`
|
|
1129
|
-
);
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
this.schemaSetupComplete = true;
|
|
1133
|
-
this.logger?.debug?.(`Schema "${this.schemaName}" is ready for use`);
|
|
1134
|
-
} catch (error) {
|
|
1135
|
-
this.schemaSetupComplete = void 0;
|
|
1136
|
-
this.setupSchemaPromise = null;
|
|
1137
|
-
throw error;
|
|
1138
|
-
} finally {
|
|
1139
|
-
this.setupSchemaPromise = null;
|
|
1140
|
-
}
|
|
1141
|
-
})();
|
|
2185
|
+
for (const indexDef of this.getDefaultIndexDefinitions()) {
|
|
2186
|
+
try {
|
|
2187
|
+
await this.db.createIndex(indexDef);
|
|
2188
|
+
} catch (error) {
|
|
2189
|
+
this.logger?.warn?.(`Failed to create index ${indexDef.name}:`, error);
|
|
2190
|
+
}
|
|
1142
2191
|
}
|
|
1143
|
-
await this.setupSchemaPromise;
|
|
1144
2192
|
}
|
|
1145
|
-
|
|
2193
|
+
/**
|
|
2194
|
+
* Creates custom user-defined indexes for this domain's tables.
|
|
2195
|
+
*/
|
|
2196
|
+
async createCustomIndexes() {
|
|
2197
|
+
if (!this.indexes || this.indexes.length === 0) {
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2200
|
+
for (const indexDef of this.indexes) {
|
|
2201
|
+
try {
|
|
2202
|
+
await this.db.createIndex(indexDef);
|
|
2203
|
+
} catch (error) {
|
|
2204
|
+
this.logger?.warn?.(`Failed to create custom index ${indexDef.name}:`, error);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
async dangerouslyClearAll() {
|
|
2209
|
+
await this.db.clearTable({ tableName: TABLE_SPANS });
|
|
2210
|
+
}
|
|
2211
|
+
get tracingStrategy() {
|
|
2212
|
+
return {
|
|
2213
|
+
preferred: "batch-with-updates",
|
|
2214
|
+
supported: ["batch-with-updates", "insert-only"]
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
async createSpan(args) {
|
|
2218
|
+
const { span } = args;
|
|
1146
2219
|
try {
|
|
1147
|
-
const
|
|
1148
|
-
const
|
|
1149
|
-
const
|
|
1150
|
-
const
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
request.input(`param${i}`, value);
|
|
1159
|
-
}
|
|
1160
|
-
});
|
|
1161
|
-
await request.query(insertSql);
|
|
2220
|
+
const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
|
|
2221
|
+
const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
|
|
2222
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2223
|
+
const record = {
|
|
2224
|
+
...span,
|
|
2225
|
+
startedAt,
|
|
2226
|
+
endedAt,
|
|
2227
|
+
createdAt: now,
|
|
2228
|
+
updatedAt: now
|
|
2229
|
+
};
|
|
2230
|
+
return this.db.insert({ tableName: TABLE_SPANS, record });
|
|
1162
2231
|
} catch (error) {
|
|
1163
2232
|
throw new MastraError(
|
|
1164
2233
|
{
|
|
1165
|
-
id: "
|
|
2234
|
+
id: createStorageErrorId("MSSQL", "CREATE_SPAN", "FAILED"),
|
|
1166
2235
|
domain: ErrorDomain.STORAGE,
|
|
1167
|
-
category: ErrorCategory.
|
|
2236
|
+
category: ErrorCategory.USER,
|
|
1168
2237
|
details: {
|
|
1169
|
-
|
|
2238
|
+
spanId: span.spanId,
|
|
2239
|
+
traceId: span.traceId,
|
|
2240
|
+
spanType: span.spanType,
|
|
2241
|
+
name: span.name
|
|
1170
2242
|
}
|
|
1171
2243
|
},
|
|
1172
2244
|
error
|
|
1173
2245
|
);
|
|
1174
2246
|
}
|
|
1175
2247
|
}
|
|
1176
|
-
async
|
|
1177
|
-
const
|
|
2248
|
+
async getTrace(args) {
|
|
2249
|
+
const { traceId } = args;
|
|
1178
2250
|
try {
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
2251
|
+
const tableName = getTableName2({
|
|
2252
|
+
indexName: TABLE_SPANS,
|
|
2253
|
+
schemaName: getSchemaName2(this.schema)
|
|
2254
|
+
});
|
|
2255
|
+
const request = this.pool.request();
|
|
2256
|
+
request.input("traceId", traceId);
|
|
2257
|
+
const result = await request.query(
|
|
2258
|
+
`SELECT
|
|
2259
|
+
[traceId], [spanId], [parentSpanId], [name],
|
|
2260
|
+
[entityType], [entityId], [entityName],
|
|
2261
|
+
[userId], [organizationId], [resourceId],
|
|
2262
|
+
[runId], [sessionId], [threadId], [requestId],
|
|
2263
|
+
[environment], [source], [serviceName], [scope],
|
|
2264
|
+
[spanType], [attributes], [metadata], [tags], [links],
|
|
2265
|
+
[input], [output], [error], [isEvent],
|
|
2266
|
+
[startedAt], [endedAt], [createdAt], [updatedAt]
|
|
2267
|
+
FROM ${tableName}
|
|
2268
|
+
WHERE [traceId] = @traceId
|
|
2269
|
+
ORDER BY [startedAt] ASC`
|
|
2270
|
+
);
|
|
2271
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
2272
|
+
return null;
|
|
1187
2273
|
}
|
|
2274
|
+
return {
|
|
2275
|
+
traceId,
|
|
2276
|
+
spans: result.recordset.map(
|
|
2277
|
+
(span) => transformFromSqlRow({
|
|
2278
|
+
tableName: TABLE_SPANS,
|
|
2279
|
+
sqlRow: span
|
|
2280
|
+
})
|
|
2281
|
+
)
|
|
2282
|
+
};
|
|
1188
2283
|
} catch (error) {
|
|
1189
2284
|
throw new MastraError(
|
|
1190
2285
|
{
|
|
1191
|
-
id: "
|
|
2286
|
+
id: createStorageErrorId("MSSQL", "GET_TRACE", "FAILED"),
|
|
1192
2287
|
domain: ErrorDomain.STORAGE,
|
|
1193
|
-
category: ErrorCategory.
|
|
2288
|
+
category: ErrorCategory.USER,
|
|
1194
2289
|
details: {
|
|
1195
|
-
|
|
2290
|
+
traceId
|
|
1196
2291
|
}
|
|
1197
2292
|
},
|
|
1198
2293
|
error
|
|
1199
2294
|
);
|
|
1200
2295
|
}
|
|
1201
2296
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
2297
|
+
async getSpan(args) {
|
|
2298
|
+
const { traceId, spanId } = args;
|
|
2299
|
+
try {
|
|
2300
|
+
const tableName = getTableName2({
|
|
2301
|
+
indexName: TABLE_SPANS,
|
|
2302
|
+
schemaName: getSchemaName2(this.schema)
|
|
2303
|
+
});
|
|
2304
|
+
const request = this.pool.request();
|
|
2305
|
+
request.input("traceId", traceId);
|
|
2306
|
+
request.input("spanId", spanId);
|
|
2307
|
+
const result = await request.query(
|
|
2308
|
+
`SELECT
|
|
2309
|
+
[traceId], [spanId], [parentSpanId], [name],
|
|
2310
|
+
[entityType], [entityId], [entityName],
|
|
2311
|
+
[userId], [organizationId], [resourceId],
|
|
2312
|
+
[runId], [sessionId], [threadId], [requestId],
|
|
2313
|
+
[environment], [source], [serviceName], [scope],
|
|
2314
|
+
[spanType], [attributes], [metadata], [tags], [links],
|
|
2315
|
+
[input], [output], [error], [isEvent],
|
|
2316
|
+
[startedAt], [endedAt], [createdAt], [updatedAt]
|
|
2317
|
+
FROM ${tableName}
|
|
2318
|
+
WHERE [traceId] = @traceId AND [spanId] = @spanId`
|
|
2319
|
+
);
|
|
2320
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
2321
|
+
return null;
|
|
2322
|
+
}
|
|
2323
|
+
return {
|
|
2324
|
+
span: transformFromSqlRow({
|
|
2325
|
+
tableName: TABLE_SPANS,
|
|
2326
|
+
sqlRow: result.recordset[0]
|
|
2327
|
+
})
|
|
2328
|
+
};
|
|
2329
|
+
} catch (error) {
|
|
2330
|
+
throw new MastraError(
|
|
2331
|
+
{
|
|
2332
|
+
id: createStorageErrorId("MSSQL", "GET_SPAN", "FAILED"),
|
|
2333
|
+
domain: ErrorDomain.STORAGE,
|
|
2334
|
+
category: ErrorCategory.USER,
|
|
2335
|
+
details: { traceId, spanId }
|
|
2336
|
+
},
|
|
2337
|
+
error
|
|
2338
|
+
);
|
|
1210
2339
|
}
|
|
1211
2340
|
}
|
|
1212
|
-
async
|
|
1213
|
-
|
|
1214
|
-
schema
|
|
1215
|
-
}) {
|
|
2341
|
+
async getRootSpan(args) {
|
|
2342
|
+
const { traceId } = args;
|
|
1216
2343
|
try {
|
|
1217
|
-
const
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
2344
|
+
const tableName = getTableName2({
|
|
2345
|
+
indexName: TABLE_SPANS,
|
|
2346
|
+
schemaName: getSchemaName2(this.schema)
|
|
2347
|
+
});
|
|
2348
|
+
const request = this.pool.request();
|
|
2349
|
+
request.input("traceId", traceId);
|
|
2350
|
+
const result = await request.query(
|
|
2351
|
+
`SELECT
|
|
2352
|
+
[traceId], [spanId], [parentSpanId], [name],
|
|
2353
|
+
[entityType], [entityId], [entityName],
|
|
2354
|
+
[userId], [organizationId], [resourceId],
|
|
2355
|
+
[runId], [sessionId], [threadId], [requestId],
|
|
2356
|
+
[environment], [source], [serviceName], [scope],
|
|
2357
|
+
[spanType], [attributes], [metadata], [tags], [links],
|
|
2358
|
+
[input], [output], [error], [isEvent],
|
|
2359
|
+
[startedAt], [endedAt], [createdAt], [updatedAt]
|
|
2360
|
+
FROM ${tableName}
|
|
2361
|
+
WHERE [traceId] = @traceId AND [parentSpanId] IS NULL`
|
|
1233
2362
|
);
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
const checkTableResult = await checkTableRequest.query(checkTableSql);
|
|
1237
|
-
const tableExists = Array.isArray(checkTableResult.recordset) && checkTableResult.recordset.length > 0;
|
|
1238
|
-
if (!tableExists) {
|
|
1239
|
-
const createSql = `CREATE TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (
|
|
1240
|
-
${columns}
|
|
1241
|
-
)`;
|
|
1242
|
-
await this.pool.request().query(createSql);
|
|
2363
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
2364
|
+
return null;
|
|
1243
2365
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
2366
|
+
return {
|
|
2367
|
+
span: transformFromSqlRow({
|
|
2368
|
+
tableName: TABLE_SPANS,
|
|
2369
|
+
sqlRow: result.recordset[0]
|
|
2370
|
+
})
|
|
2371
|
+
};
|
|
2372
|
+
} catch (error) {
|
|
2373
|
+
throw new MastraError(
|
|
2374
|
+
{
|
|
2375
|
+
id: createStorageErrorId("MSSQL", "GET_ROOT_SPAN", "FAILED"),
|
|
2376
|
+
domain: ErrorDomain.STORAGE,
|
|
2377
|
+
category: ErrorCategory.USER,
|
|
2378
|
+
details: { traceId }
|
|
2379
|
+
},
|
|
2380
|
+
error
|
|
1254
2381
|
);
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
async updateSpan(args) {
|
|
2385
|
+
const { traceId, spanId, updates } = args;
|
|
2386
|
+
try {
|
|
2387
|
+
const data = { ...updates };
|
|
2388
|
+
if (data.endedAt instanceof Date) {
|
|
2389
|
+
data.endedAt = data.endedAt.toISOString();
|
|
1260
2390
|
}
|
|
1261
|
-
if (
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
2391
|
+
if (data.startedAt instanceof Date) {
|
|
2392
|
+
data.startedAt = data.startedAt.toISOString();
|
|
2393
|
+
}
|
|
2394
|
+
data.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2395
|
+
await this.db.update({
|
|
2396
|
+
tableName: TABLE_SPANS,
|
|
2397
|
+
keys: { spanId, traceId },
|
|
2398
|
+
data
|
|
2399
|
+
});
|
|
2400
|
+
} catch (error) {
|
|
2401
|
+
throw new MastraError(
|
|
2402
|
+
{
|
|
2403
|
+
id: createStorageErrorId("MSSQL", "UPDATE_SPAN", "FAILED"),
|
|
2404
|
+
domain: ErrorDomain.STORAGE,
|
|
2405
|
+
category: ErrorCategory.USER,
|
|
2406
|
+
details: {
|
|
2407
|
+
spanId,
|
|
2408
|
+
traceId
|
|
2409
|
+
}
|
|
2410
|
+
},
|
|
2411
|
+
error
|
|
2412
|
+
);
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
async listTraces(args) {
|
|
2416
|
+
const { filters, pagination, orderBy } = listTracesArgsSchema.parse(args);
|
|
2417
|
+
const { page, perPage } = pagination;
|
|
2418
|
+
const tableName = getTableName2({
|
|
2419
|
+
indexName: TABLE_SPANS,
|
|
2420
|
+
schemaName: getSchemaName2(this.schema)
|
|
2421
|
+
});
|
|
2422
|
+
try {
|
|
2423
|
+
const conditions = ["r.[parentSpanId] IS NULL"];
|
|
2424
|
+
const params = {};
|
|
2425
|
+
let paramIndex = 1;
|
|
2426
|
+
if (filters) {
|
|
2427
|
+
if (filters.startedAt?.start) {
|
|
2428
|
+
const param = `p${paramIndex++}`;
|
|
2429
|
+
conditions.push(`r.[startedAt] >= @${param}`);
|
|
2430
|
+
params[param] = filters.startedAt.start.toISOString();
|
|
2431
|
+
}
|
|
2432
|
+
if (filters.startedAt?.end) {
|
|
2433
|
+
const param = `p${paramIndex++}`;
|
|
2434
|
+
conditions.push(`r.[startedAt] <= @${param}`);
|
|
2435
|
+
params[param] = filters.startedAt.end.toISOString();
|
|
2436
|
+
}
|
|
2437
|
+
if (filters.endedAt?.start) {
|
|
2438
|
+
const param = `p${paramIndex++}`;
|
|
2439
|
+
conditions.push(`r.[endedAt] >= @${param}`);
|
|
2440
|
+
params[param] = filters.endedAt.start.toISOString();
|
|
2441
|
+
}
|
|
2442
|
+
if (filters.endedAt?.end) {
|
|
2443
|
+
const param = `p${paramIndex++}`;
|
|
2444
|
+
conditions.push(`r.[endedAt] <= @${param}`);
|
|
2445
|
+
params[param] = filters.endedAt.end.toISOString();
|
|
2446
|
+
}
|
|
2447
|
+
if (filters.spanType !== void 0) {
|
|
2448
|
+
const param = `p${paramIndex++}`;
|
|
2449
|
+
conditions.push(`r.[spanType] = @${param}`);
|
|
2450
|
+
params[param] = filters.spanType;
|
|
2451
|
+
}
|
|
2452
|
+
if (filters.entityType !== void 0) {
|
|
2453
|
+
const param = `p${paramIndex++}`;
|
|
2454
|
+
conditions.push(`r.[entityType] = @${param}`);
|
|
2455
|
+
params[param] = filters.entityType;
|
|
2456
|
+
}
|
|
2457
|
+
if (filters.entityId !== void 0) {
|
|
2458
|
+
const param = `p${paramIndex++}`;
|
|
2459
|
+
conditions.push(`r.[entityId] = @${param}`);
|
|
2460
|
+
params[param] = filters.entityId;
|
|
2461
|
+
}
|
|
2462
|
+
if (filters.entityName !== void 0) {
|
|
2463
|
+
const param = `p${paramIndex++}`;
|
|
2464
|
+
conditions.push(`r.[entityName] = @${param}`);
|
|
2465
|
+
params[param] = filters.entityName;
|
|
2466
|
+
}
|
|
2467
|
+
if (filters.userId !== void 0) {
|
|
2468
|
+
const param = `p${paramIndex++}`;
|
|
2469
|
+
conditions.push(`r.[userId] = @${param}`);
|
|
2470
|
+
params[param] = filters.userId;
|
|
2471
|
+
}
|
|
2472
|
+
if (filters.organizationId !== void 0) {
|
|
2473
|
+
const param = `p${paramIndex++}`;
|
|
2474
|
+
conditions.push(`r.[organizationId] = @${param}`);
|
|
2475
|
+
params[param] = filters.organizationId;
|
|
2476
|
+
}
|
|
2477
|
+
if (filters.resourceId !== void 0) {
|
|
2478
|
+
const param = `p${paramIndex++}`;
|
|
2479
|
+
conditions.push(`r.[resourceId] = @${param}`);
|
|
2480
|
+
params[param] = filters.resourceId;
|
|
2481
|
+
}
|
|
2482
|
+
if (filters.runId !== void 0) {
|
|
2483
|
+
const param = `p${paramIndex++}`;
|
|
2484
|
+
conditions.push(`r.[runId] = @${param}`);
|
|
2485
|
+
params[param] = filters.runId;
|
|
2486
|
+
}
|
|
2487
|
+
if (filters.sessionId !== void 0) {
|
|
2488
|
+
const param = `p${paramIndex++}`;
|
|
2489
|
+
conditions.push(`r.[sessionId] = @${param}`);
|
|
2490
|
+
params[param] = filters.sessionId;
|
|
2491
|
+
}
|
|
2492
|
+
if (filters.threadId !== void 0) {
|
|
2493
|
+
const param = `p${paramIndex++}`;
|
|
2494
|
+
conditions.push(`r.[threadId] = @${param}`);
|
|
2495
|
+
params[param] = filters.threadId;
|
|
2496
|
+
}
|
|
2497
|
+
if (filters.requestId !== void 0) {
|
|
2498
|
+
const param = `p${paramIndex++}`;
|
|
2499
|
+
conditions.push(`r.[requestId] = @${param}`);
|
|
2500
|
+
params[param] = filters.requestId;
|
|
2501
|
+
}
|
|
2502
|
+
if (filters.environment !== void 0) {
|
|
2503
|
+
const param = `p${paramIndex++}`;
|
|
2504
|
+
conditions.push(`r.[environment] = @${param}`);
|
|
2505
|
+
params[param] = filters.environment;
|
|
1271
2506
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
2507
|
+
if (filters.source !== void 0) {
|
|
2508
|
+
const param = `p${paramIndex++}`;
|
|
2509
|
+
conditions.push(`r.[source] = @${param}`);
|
|
2510
|
+
params[param] = filters.source;
|
|
2511
|
+
}
|
|
2512
|
+
if (filters.serviceName !== void 0) {
|
|
2513
|
+
const param = `p${paramIndex++}`;
|
|
2514
|
+
conditions.push(`r.[serviceName] = @${param}`);
|
|
2515
|
+
params[param] = filters.serviceName;
|
|
2516
|
+
}
|
|
2517
|
+
if (filters.scope != null) {
|
|
2518
|
+
for (const [key, value] of Object.entries(filters.scope)) {
|
|
2519
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
|
|
2520
|
+
throw new MastraError({
|
|
2521
|
+
id: createStorageErrorId("MSSQL", "LIST_TRACES", "INVALID_FILTER_KEY"),
|
|
2522
|
+
domain: ErrorDomain.STORAGE,
|
|
2523
|
+
category: ErrorCategory.USER,
|
|
2524
|
+
details: { key }
|
|
2525
|
+
});
|
|
2526
|
+
}
|
|
2527
|
+
const param = `p${paramIndex++}`;
|
|
2528
|
+
conditions.push(`JSON_VALUE(r.[scope], '$.${key}') = @${param}`);
|
|
2529
|
+
params[param] = typeof value === "string" ? value : JSON.stringify(value);
|
|
1281
2530
|
}
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
columnCheckRequest.input("tableName", fullTableName.replace(/[[\]]/g, "").split(".").pop());
|
|
1304
|
-
columnCheckRequest.input("columnName", columnName);
|
|
1305
|
-
columnCheckRequest.input("schema", this.schemaName || "dbo");
|
|
1306
|
-
const checkSql = `SELECT 1 AS found FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @tableName AND COLUMN_NAME = @columnName`;
|
|
1307
|
-
const checkResult = await columnCheckRequest.query(checkSql);
|
|
1308
|
-
const columnExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
1309
|
-
if (!columnExists) {
|
|
1310
|
-
const columnDef = schema[columnName];
|
|
1311
|
-
const sqlType = this.getSqlType(columnDef.type);
|
|
1312
|
-
const nullable = columnDef.nullable === false ? "NOT NULL" : "";
|
|
1313
|
-
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
1314
|
-
const parsedColumnName = parseSqlIdentifier(columnName, "column name");
|
|
1315
|
-
const alterSql = `ALTER TABLE ${fullTableName} ADD [${parsedColumnName}] ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
1316
|
-
await this.pool.request().query(alterSql);
|
|
1317
|
-
this.logger?.debug?.(`Ensured column ${parsedColumnName} exists in table ${fullTableName}`);
|
|
2531
|
+
}
|
|
2532
|
+
if (filters.metadata != null) {
|
|
2533
|
+
for (const [key, value] of Object.entries(filters.metadata)) {
|
|
2534
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
|
|
2535
|
+
throw new MastraError({
|
|
2536
|
+
id: createStorageErrorId("MSSQL", "LIST_TRACES", "INVALID_FILTER_KEY"),
|
|
2537
|
+
domain: ErrorDomain.STORAGE,
|
|
2538
|
+
category: ErrorCategory.USER,
|
|
2539
|
+
details: { key }
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
2542
|
+
const param = `p${paramIndex++}`;
|
|
2543
|
+
conditions.push(`JSON_VALUE(r.[metadata], '$.${key}') = @${param}`);
|
|
2544
|
+
params[param] = typeof value === "string" ? value : JSON.stringify(value);
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
if (filters.tags != null && filters.tags.length > 0) {
|
|
2548
|
+
for (const tag of filters.tags) {
|
|
2549
|
+
const param = `p${paramIndex++}`;
|
|
2550
|
+
conditions.push(`EXISTS (SELECT 1 FROM OPENJSON(r.[tags]) WHERE [value] = @${param})`);
|
|
2551
|
+
params[param] = tag;
|
|
1318
2552
|
}
|
|
1319
2553
|
}
|
|
2554
|
+
if (filters.status !== void 0) {
|
|
2555
|
+
switch (filters.status) {
|
|
2556
|
+
case TraceStatus.ERROR:
|
|
2557
|
+
conditions.push(`r.[error] IS NOT NULL`);
|
|
2558
|
+
break;
|
|
2559
|
+
case TraceStatus.RUNNING:
|
|
2560
|
+
conditions.push(`r.[endedAt] IS NULL AND r.[error] IS NULL`);
|
|
2561
|
+
break;
|
|
2562
|
+
case TraceStatus.SUCCESS:
|
|
2563
|
+
conditions.push(`r.[endedAt] IS NOT NULL AND r.[error] IS NULL`);
|
|
2564
|
+
break;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
if (filters.hasChildError !== void 0) {
|
|
2568
|
+
if (filters.hasChildError) {
|
|
2569
|
+
conditions.push(`EXISTS (
|
|
2570
|
+
SELECT 1 FROM ${tableName} c
|
|
2571
|
+
WHERE c.[traceId] = r.[traceId] AND c.[error] IS NOT NULL
|
|
2572
|
+
)`);
|
|
2573
|
+
} else {
|
|
2574
|
+
conditions.push(`NOT EXISTS (
|
|
2575
|
+
SELECT 1 FROM ${tableName} c
|
|
2576
|
+
WHERE c.[traceId] = r.[traceId] AND c.[error] IS NOT NULL
|
|
2577
|
+
)`);
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2582
|
+
const sortField = orderBy.field;
|
|
2583
|
+
const sortDirection = orderBy.direction;
|
|
2584
|
+
const countRequest = this.pool.request();
|
|
2585
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2586
|
+
countRequest.input(key, value);
|
|
2587
|
+
});
|
|
2588
|
+
const countResult = await countRequest.query(
|
|
2589
|
+
`SELECT COUNT(*) as count FROM ${tableName} r ${whereClause}`
|
|
2590
|
+
);
|
|
2591
|
+
const count = countResult.recordset[0]?.count ?? 0;
|
|
2592
|
+
if (count === 0) {
|
|
2593
|
+
return {
|
|
2594
|
+
pagination: {
|
|
2595
|
+
total: 0,
|
|
2596
|
+
page,
|
|
2597
|
+
perPage,
|
|
2598
|
+
hasMore: false
|
|
2599
|
+
},
|
|
2600
|
+
spans: []
|
|
2601
|
+
};
|
|
1320
2602
|
}
|
|
2603
|
+
const dataRequest = this.pool.request();
|
|
2604
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2605
|
+
dataRequest.input(key, value);
|
|
2606
|
+
});
|
|
2607
|
+
dataRequest.input("offset", page * perPage);
|
|
2608
|
+
dataRequest.input("limit", perPage);
|
|
2609
|
+
const result = await dataRequest.query(
|
|
2610
|
+
`SELECT
|
|
2611
|
+
r.[traceId], r.[spanId], r.[parentSpanId], r.[name],
|
|
2612
|
+
r.[entityType], r.[entityId], r.[entityName],
|
|
2613
|
+
r.[userId], r.[organizationId], r.[resourceId],
|
|
2614
|
+
r.[runId], r.[sessionId], r.[threadId], r.[requestId],
|
|
2615
|
+
r.[environment], r.[source], r.[serviceName], r.[scope],
|
|
2616
|
+
r.[spanType], r.[attributes], r.[metadata], r.[tags], r.[links],
|
|
2617
|
+
r.[input], r.[output], r.[error], r.[isEvent],
|
|
2618
|
+
r.[startedAt], r.[endedAt], r.[createdAt], r.[updatedAt]
|
|
2619
|
+
FROM ${tableName} r
|
|
2620
|
+
${whereClause}
|
|
2621
|
+
ORDER BY r.[${sortField}] ${sortDirection}
|
|
2622
|
+
OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
|
|
2623
|
+
);
|
|
2624
|
+
return {
|
|
2625
|
+
pagination: {
|
|
2626
|
+
total: count,
|
|
2627
|
+
page,
|
|
2628
|
+
perPage,
|
|
2629
|
+
hasMore: (page + 1) * perPage < count
|
|
2630
|
+
},
|
|
2631
|
+
spans: result.recordset.map(
|
|
2632
|
+
(span) => transformFromSqlRow({
|
|
2633
|
+
tableName: TABLE_SPANS,
|
|
2634
|
+
sqlRow: span
|
|
2635
|
+
})
|
|
2636
|
+
)
|
|
2637
|
+
};
|
|
1321
2638
|
} catch (error) {
|
|
1322
2639
|
throw new MastraError(
|
|
1323
2640
|
{
|
|
1324
|
-
id: "
|
|
2641
|
+
id: createStorageErrorId("MSSQL", "LIST_TRACES", "FAILED"),
|
|
1325
2642
|
domain: ErrorDomain.STORAGE,
|
|
1326
|
-
category: ErrorCategory.
|
|
1327
|
-
details: {
|
|
1328
|
-
tableName
|
|
1329
|
-
}
|
|
2643
|
+
category: ErrorCategory.USER
|
|
1330
2644
|
},
|
|
1331
2645
|
error
|
|
1332
2646
|
);
|
|
1333
2647
|
}
|
|
1334
2648
|
}
|
|
1335
|
-
async
|
|
2649
|
+
async batchCreateSpans(args) {
|
|
2650
|
+
if (!args.records || args.records.length === 0) {
|
|
2651
|
+
return;
|
|
2652
|
+
}
|
|
1336
2653
|
try {
|
|
1337
|
-
const
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
2654
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2655
|
+
await this.db.batchInsert({
|
|
2656
|
+
tableName: TABLE_SPANS,
|
|
2657
|
+
records: args.records.map((span) => ({
|
|
2658
|
+
...span,
|
|
2659
|
+
startedAt: span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt,
|
|
2660
|
+
endedAt: span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt,
|
|
2661
|
+
createdAt: now,
|
|
2662
|
+
updatedAt: now
|
|
2663
|
+
}))
|
|
1344
2664
|
});
|
|
1345
|
-
const resultSet = await request.query(sql7);
|
|
1346
|
-
const result = resultSet.recordset[0] || null;
|
|
1347
|
-
if (!result) {
|
|
1348
|
-
return null;
|
|
1349
|
-
}
|
|
1350
|
-
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
1351
|
-
const snapshot = result;
|
|
1352
|
-
if (typeof snapshot.snapshot === "string") {
|
|
1353
|
-
snapshot.snapshot = JSON.parse(snapshot.snapshot);
|
|
1354
|
-
}
|
|
1355
|
-
return snapshot;
|
|
1356
|
-
}
|
|
1357
|
-
return result;
|
|
1358
2665
|
} catch (error) {
|
|
1359
2666
|
throw new MastraError(
|
|
1360
2667
|
{
|
|
1361
|
-
id: "
|
|
2668
|
+
id: createStorageErrorId("MSSQL", "BATCH_CREATE_SPANS", "FAILED"),
|
|
1362
2669
|
domain: ErrorDomain.STORAGE,
|
|
1363
|
-
category: ErrorCategory.
|
|
2670
|
+
category: ErrorCategory.USER,
|
|
1364
2671
|
details: {
|
|
1365
|
-
|
|
2672
|
+
count: args.records.length
|
|
1366
2673
|
}
|
|
1367
2674
|
},
|
|
1368
2675
|
error
|
|
1369
2676
|
);
|
|
1370
2677
|
}
|
|
1371
2678
|
}
|
|
1372
|
-
async
|
|
1373
|
-
|
|
2679
|
+
async batchUpdateSpans(args) {
|
|
2680
|
+
if (!args.records || args.records.length === 0) {
|
|
2681
|
+
return;
|
|
2682
|
+
}
|
|
2683
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1374
2684
|
try {
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
2685
|
+
const updates = args.records.map(({ traceId, spanId, updates: data }) => {
|
|
2686
|
+
const processedData = { ...data };
|
|
2687
|
+
if (processedData.endedAt instanceof Date) {
|
|
2688
|
+
processedData.endedAt = processedData.endedAt.toISOString();
|
|
2689
|
+
}
|
|
2690
|
+
if (processedData.startedAt instanceof Date) {
|
|
2691
|
+
processedData.startedAt = processedData.startedAt.toISOString();
|
|
2692
|
+
}
|
|
2693
|
+
processedData.updatedAt = now;
|
|
2694
|
+
return {
|
|
2695
|
+
keys: { spanId, traceId },
|
|
2696
|
+
data: processedData
|
|
2697
|
+
};
|
|
2698
|
+
});
|
|
2699
|
+
await this.db.batchUpdate({
|
|
2700
|
+
tableName: TABLE_SPANS,
|
|
2701
|
+
updates
|
|
2702
|
+
});
|
|
1380
2703
|
} catch (error) {
|
|
1381
|
-
await transaction.rollback();
|
|
1382
2704
|
throw new MastraError(
|
|
1383
2705
|
{
|
|
1384
|
-
id: "
|
|
2706
|
+
id: createStorageErrorId("MSSQL", "BATCH_UPDATE_SPANS", "FAILED"),
|
|
1385
2707
|
domain: ErrorDomain.STORAGE,
|
|
1386
|
-
category: ErrorCategory.
|
|
2708
|
+
category: ErrorCategory.USER,
|
|
1387
2709
|
details: {
|
|
1388
|
-
|
|
1389
|
-
numberOfRecords: records.length
|
|
2710
|
+
count: args.records.length
|
|
1390
2711
|
}
|
|
1391
2712
|
},
|
|
1392
2713
|
error
|
|
1393
2714
|
);
|
|
1394
2715
|
}
|
|
1395
2716
|
}
|
|
1396
|
-
async
|
|
2717
|
+
async batchDeleteTraces(args) {
|
|
2718
|
+
if (!args.traceIds || args.traceIds.length === 0) {
|
|
2719
|
+
return;
|
|
2720
|
+
}
|
|
1397
2721
|
try {
|
|
1398
|
-
const
|
|
1399
|
-
await this.
|
|
2722
|
+
const keys = args.traceIds.map((traceId) => ({ traceId }));
|
|
2723
|
+
await this.db.batchDelete({
|
|
2724
|
+
tableName: TABLE_SPANS,
|
|
2725
|
+
keys
|
|
2726
|
+
});
|
|
1400
2727
|
} catch (error) {
|
|
1401
2728
|
throw new MastraError(
|
|
1402
2729
|
{
|
|
1403
|
-
id: "
|
|
2730
|
+
id: createStorageErrorId("MSSQL", "BATCH_DELETE_TRACES", "FAILED"),
|
|
1404
2731
|
domain: ErrorDomain.STORAGE,
|
|
1405
|
-
category: ErrorCategory.
|
|
2732
|
+
category: ErrorCategory.USER,
|
|
1406
2733
|
details: {
|
|
1407
|
-
|
|
2734
|
+
count: args.traceIds.length
|
|
1408
2735
|
}
|
|
1409
2736
|
},
|
|
1410
2737
|
error
|
|
@@ -1412,49 +2739,92 @@ ${columns}
|
|
|
1412
2739
|
}
|
|
1413
2740
|
}
|
|
1414
2741
|
};
|
|
1415
|
-
function parseJSON(jsonString) {
|
|
1416
|
-
try {
|
|
1417
|
-
return JSON.parse(jsonString);
|
|
1418
|
-
} catch {
|
|
1419
|
-
return jsonString;
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
2742
|
function transformScoreRow(row) {
|
|
1423
|
-
return {
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
scorer: parseJSON(row.scorer),
|
|
1427
|
-
preprocessStepResult: parseJSON(row.preprocessStepResult),
|
|
1428
|
-
analyzeStepResult: parseJSON(row.analyzeStepResult),
|
|
1429
|
-
metadata: parseJSON(row.metadata),
|
|
1430
|
-
output: parseJSON(row.output),
|
|
1431
|
-
additionalContext: parseJSON(row.additionalContext),
|
|
1432
|
-
runtimeContext: parseJSON(row.runtimeContext),
|
|
1433
|
-
entity: parseJSON(row.entity),
|
|
1434
|
-
createdAt: row.createdAt,
|
|
1435
|
-
updatedAt: row.updatedAt
|
|
1436
|
-
};
|
|
2743
|
+
return transformScoreRow$1(row, {
|
|
2744
|
+
convertTimestamps: true
|
|
2745
|
+
});
|
|
1437
2746
|
}
|
|
1438
|
-
var ScoresMSSQL = class extends ScoresStorage {
|
|
2747
|
+
var ScoresMSSQL = class _ScoresMSSQL extends ScoresStorage {
|
|
1439
2748
|
pool;
|
|
1440
|
-
|
|
2749
|
+
db;
|
|
1441
2750
|
schema;
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
2751
|
+
needsConnect;
|
|
2752
|
+
skipDefaultIndexes;
|
|
2753
|
+
indexes;
|
|
2754
|
+
/** Tables managed by this domain */
|
|
2755
|
+
static MANAGED_TABLES = [TABLE_SCORERS];
|
|
2756
|
+
constructor(config) {
|
|
1447
2757
|
super();
|
|
2758
|
+
const { pool, schemaName, skipDefaultIndexes, indexes, needsConnect } = resolveMssqlConfig(config);
|
|
1448
2759
|
this.pool = pool;
|
|
1449
|
-
this.
|
|
1450
|
-
this.
|
|
2760
|
+
this.schema = schemaName;
|
|
2761
|
+
this.db = new MssqlDB({ pool, schemaName, skipDefaultIndexes });
|
|
2762
|
+
this.needsConnect = needsConnect;
|
|
2763
|
+
this.skipDefaultIndexes = skipDefaultIndexes;
|
|
2764
|
+
this.indexes = indexes?.filter((idx) => _ScoresMSSQL.MANAGED_TABLES.includes(idx.table));
|
|
2765
|
+
}
|
|
2766
|
+
async init() {
|
|
2767
|
+
if (this.needsConnect) {
|
|
2768
|
+
await this.pool.connect();
|
|
2769
|
+
this.needsConnect = false;
|
|
2770
|
+
}
|
|
2771
|
+
await this.db.createTable({ tableName: TABLE_SCORERS, schema: TABLE_SCHEMAS[TABLE_SCORERS] });
|
|
2772
|
+
await this.createDefaultIndexes();
|
|
2773
|
+
await this.createCustomIndexes();
|
|
2774
|
+
}
|
|
2775
|
+
/**
|
|
2776
|
+
* Returns default index definitions for the scores domain tables.
|
|
2777
|
+
* IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
|
|
2778
|
+
*/
|
|
2779
|
+
getDefaultIndexDefinitions() {
|
|
2780
|
+
const schemaPrefix = this.schema ? `${this.schema}_` : "";
|
|
2781
|
+
return [
|
|
2782
|
+
{
|
|
2783
|
+
name: `${schemaPrefix}mastra_scores_trace_id_span_id_seqid_idx`,
|
|
2784
|
+
table: TABLE_SCORERS,
|
|
2785
|
+
columns: ["traceId", "spanId", "seq_id DESC"]
|
|
2786
|
+
}
|
|
2787
|
+
];
|
|
2788
|
+
}
|
|
2789
|
+
/**
|
|
2790
|
+
* Creates default indexes for optimal query performance.
|
|
2791
|
+
*/
|
|
2792
|
+
async createDefaultIndexes() {
|
|
2793
|
+
if (this.skipDefaultIndexes) {
|
|
2794
|
+
return;
|
|
2795
|
+
}
|
|
2796
|
+
for (const indexDef of this.getDefaultIndexDefinitions()) {
|
|
2797
|
+
try {
|
|
2798
|
+
await this.db.createIndex(indexDef);
|
|
2799
|
+
} catch (error) {
|
|
2800
|
+
this.logger?.warn?.(`Failed to create index ${indexDef.name}:`, error);
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
/**
|
|
2805
|
+
* Creates custom user-defined indexes for this domain's tables.
|
|
2806
|
+
*/
|
|
2807
|
+
async createCustomIndexes() {
|
|
2808
|
+
if (!this.indexes || this.indexes.length === 0) {
|
|
2809
|
+
return;
|
|
2810
|
+
}
|
|
2811
|
+
for (const indexDef of this.indexes) {
|
|
2812
|
+
try {
|
|
2813
|
+
await this.db.createIndex(indexDef);
|
|
2814
|
+
} catch (error) {
|
|
2815
|
+
this.logger?.warn?.(`Failed to create custom index ${indexDef.name}:`, error);
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
async dangerouslyClearAll() {
|
|
2820
|
+
await this.db.clearTable({ tableName: TABLE_SCORERS });
|
|
1451
2821
|
}
|
|
1452
2822
|
async getScoreById({ id }) {
|
|
1453
2823
|
try {
|
|
1454
2824
|
const request = this.pool.request();
|
|
1455
2825
|
request.input("p1", id);
|
|
1456
2826
|
const result = await request.query(
|
|
1457
|
-
`SELECT * FROM ${
|
|
2827
|
+
`SELECT * FROM ${getTableName2({ indexName: TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE id = @p1`
|
|
1458
2828
|
);
|
|
1459
2829
|
if (result.recordset.length === 0) {
|
|
1460
2830
|
return null;
|
|
@@ -1463,7 +2833,7 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1463
2833
|
} catch (error) {
|
|
1464
2834
|
throw new MastraError(
|
|
1465
2835
|
{
|
|
1466
|
-
id: "
|
|
2836
|
+
id: createStorageErrorId("MSSQL", "GET_SCORE_BY_ID", "FAILED"),
|
|
1467
2837
|
domain: ErrorDomain.STORAGE,
|
|
1468
2838
|
category: ErrorCategory.THIRD_PARTY,
|
|
1469
2839
|
details: { id }
|
|
@@ -1473,8 +2843,29 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1473
2843
|
}
|
|
1474
2844
|
}
|
|
1475
2845
|
async saveScore(score) {
|
|
2846
|
+
let validatedScore;
|
|
2847
|
+
try {
|
|
2848
|
+
validatedScore = saveScorePayloadSchema.parse(score);
|
|
2849
|
+
} catch (error) {
|
|
2850
|
+
throw new MastraError(
|
|
2851
|
+
{
|
|
2852
|
+
id: createStorageErrorId("MSSQL", "SAVE_SCORE", "VALIDATION_FAILED"),
|
|
2853
|
+
domain: ErrorDomain.STORAGE,
|
|
2854
|
+
category: ErrorCategory.USER,
|
|
2855
|
+
details: {
|
|
2856
|
+
scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
|
|
2857
|
+
entityId: score.entityId ?? "unknown",
|
|
2858
|
+
entityType: score.entityType ?? "unknown",
|
|
2859
|
+
traceId: score.traceId ?? "",
|
|
2860
|
+
spanId: score.spanId ?? ""
|
|
2861
|
+
}
|
|
2862
|
+
},
|
|
2863
|
+
error
|
|
2864
|
+
);
|
|
2865
|
+
}
|
|
1476
2866
|
try {
|
|
1477
|
-
const scoreId =
|
|
2867
|
+
const scoreId = randomUUID();
|
|
2868
|
+
const now = /* @__PURE__ */ new Date();
|
|
1478
2869
|
const {
|
|
1479
2870
|
scorer,
|
|
1480
2871
|
preprocessStepResult,
|
|
@@ -1483,34 +2874,33 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1483
2874
|
input,
|
|
1484
2875
|
output,
|
|
1485
2876
|
additionalContext,
|
|
1486
|
-
|
|
2877
|
+
requestContext,
|
|
1487
2878
|
entity,
|
|
1488
2879
|
...rest
|
|
1489
|
-
} =
|
|
1490
|
-
await this.
|
|
2880
|
+
} = validatedScore;
|
|
2881
|
+
await this.db.insert({
|
|
1491
2882
|
tableName: TABLE_SCORERS,
|
|
1492
2883
|
record: {
|
|
1493
2884
|
id: scoreId,
|
|
1494
2885
|
...rest,
|
|
1495
|
-
input:
|
|
1496
|
-
output:
|
|
1497
|
-
preprocessStepResult: preprocessStepResult
|
|
1498
|
-
analyzeStepResult: analyzeStepResult
|
|
1499
|
-
metadata: metadata
|
|
1500
|
-
additionalContext: additionalContext
|
|
1501
|
-
|
|
1502
|
-
entity: entity
|
|
1503
|
-
scorer: scorer
|
|
1504
|
-
createdAt:
|
|
1505
|
-
updatedAt:
|
|
2886
|
+
input: input || "",
|
|
2887
|
+
output: output || "",
|
|
2888
|
+
preprocessStepResult: preprocessStepResult || null,
|
|
2889
|
+
analyzeStepResult: analyzeStepResult || null,
|
|
2890
|
+
metadata: metadata || null,
|
|
2891
|
+
additionalContext: additionalContext || null,
|
|
2892
|
+
requestContext: requestContext || null,
|
|
2893
|
+
entity: entity || null,
|
|
2894
|
+
scorer: scorer || null,
|
|
2895
|
+
createdAt: now.toISOString(),
|
|
2896
|
+
updatedAt: now.toISOString()
|
|
1506
2897
|
}
|
|
1507
2898
|
});
|
|
1508
|
-
|
|
1509
|
-
return { score: scoreFromDb };
|
|
2899
|
+
return { score: { ...validatedScore, id: scoreId, createdAt: now, updatedAt: now } };
|
|
1510
2900
|
} catch (error) {
|
|
1511
2901
|
throw new MastraError(
|
|
1512
2902
|
{
|
|
1513
|
-
id: "
|
|
2903
|
+
id: createStorageErrorId("MSSQL", "SAVE_SCORE", "FAILED"),
|
|
1514
2904
|
domain: ErrorDomain.STORAGE,
|
|
1515
2905
|
category: ErrorCategory.THIRD_PARTY
|
|
1516
2906
|
},
|
|
@@ -1518,48 +2908,77 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1518
2908
|
);
|
|
1519
2909
|
}
|
|
1520
2910
|
}
|
|
1521
|
-
async
|
|
2911
|
+
async listScoresByScorerId({
|
|
1522
2912
|
scorerId,
|
|
1523
|
-
pagination
|
|
2913
|
+
pagination,
|
|
2914
|
+
entityId,
|
|
2915
|
+
entityType,
|
|
2916
|
+
source
|
|
1524
2917
|
}) {
|
|
1525
2918
|
try {
|
|
1526
|
-
const
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
2919
|
+
const conditions = ["[scorerId] = @p1"];
|
|
2920
|
+
const params = { p1: scorerId };
|
|
2921
|
+
let paramIndex = 2;
|
|
2922
|
+
if (entityId) {
|
|
2923
|
+
conditions.push(`[entityId] = @p${paramIndex}`);
|
|
2924
|
+
params[`p${paramIndex}`] = entityId;
|
|
2925
|
+
paramIndex++;
|
|
2926
|
+
}
|
|
2927
|
+
if (entityType) {
|
|
2928
|
+
conditions.push(`[entityType] = @p${paramIndex}`);
|
|
2929
|
+
params[`p${paramIndex}`] = entityType;
|
|
2930
|
+
paramIndex++;
|
|
2931
|
+
}
|
|
2932
|
+
if (source) {
|
|
2933
|
+
conditions.push(`[source] = @p${paramIndex}`);
|
|
2934
|
+
params[`p${paramIndex}`] = source;
|
|
2935
|
+
paramIndex++;
|
|
2936
|
+
}
|
|
2937
|
+
const whereClause = conditions.join(" AND ");
|
|
2938
|
+
const tableName = getTableName2({ indexName: TABLE_SCORERS, schemaName: getSchemaName2(this.schema) });
|
|
2939
|
+
const countRequest = this.pool.request();
|
|
2940
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2941
|
+
countRequest.input(key, value);
|
|
2942
|
+
});
|
|
2943
|
+
const totalResult = await countRequest.query(`SELECT COUNT(*) as count FROM ${tableName} WHERE ${whereClause}`);
|
|
1531
2944
|
const total = totalResult.recordset[0]?.count || 0;
|
|
2945
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1532
2946
|
if (total === 0) {
|
|
1533
2947
|
return {
|
|
1534
2948
|
pagination: {
|
|
1535
2949
|
total: 0,
|
|
1536
|
-
page
|
|
1537
|
-
perPage:
|
|
2950
|
+
page,
|
|
2951
|
+
perPage: perPageInput,
|
|
1538
2952
|
hasMore: false
|
|
1539
2953
|
},
|
|
1540
2954
|
scores: []
|
|
1541
2955
|
};
|
|
1542
2956
|
}
|
|
2957
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
2958
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2959
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
2960
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1543
2961
|
const dataRequest = this.pool.request();
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
2962
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2963
|
+
dataRequest.input(key, value);
|
|
2964
|
+
});
|
|
2965
|
+
dataRequest.input("perPage", limitValue);
|
|
2966
|
+
dataRequest.input("offset", start);
|
|
2967
|
+
const dataQuery = `SELECT * FROM ${tableName} WHERE ${whereClause} ORDER BY [createdAt] DESC OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
2968
|
+
const result = await dataRequest.query(dataQuery);
|
|
1550
2969
|
return {
|
|
1551
2970
|
pagination: {
|
|
1552
2971
|
total: Number(total),
|
|
1553
|
-
page
|
|
1554
|
-
perPage:
|
|
1555
|
-
hasMore:
|
|
2972
|
+
page,
|
|
2973
|
+
perPage: perPageForResponse,
|
|
2974
|
+
hasMore: end < total
|
|
1556
2975
|
},
|
|
1557
2976
|
scores: result.recordset.map((row) => transformScoreRow(row))
|
|
1558
2977
|
};
|
|
1559
2978
|
} catch (error) {
|
|
1560
2979
|
throw new MastraError(
|
|
1561
2980
|
{
|
|
1562
|
-
id: "
|
|
2981
|
+
id: createStorageErrorId("MSSQL", "LIST_SCORES_BY_SCORER_ID", "FAILED"),
|
|
1563
2982
|
domain: ErrorDomain.STORAGE,
|
|
1564
2983
|
category: ErrorCategory.THIRD_PARTY,
|
|
1565
2984
|
details: { scorerId }
|
|
@@ -1568,7 +2987,7 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1568
2987
|
);
|
|
1569
2988
|
}
|
|
1570
2989
|
}
|
|
1571
|
-
async
|
|
2990
|
+
async listScoresByRunId({
|
|
1572
2991
|
runId,
|
|
1573
2992
|
pagination
|
|
1574
2993
|
}) {
|
|
@@ -1576,40 +2995,45 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1576
2995
|
const request = this.pool.request();
|
|
1577
2996
|
request.input("p1", runId);
|
|
1578
2997
|
const totalResult = await request.query(
|
|
1579
|
-
`SELECT COUNT(*) as count FROM ${
|
|
2998
|
+
`SELECT COUNT(*) as count FROM ${getTableName2({ indexName: TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE [runId] = @p1`
|
|
1580
2999
|
);
|
|
1581
3000
|
const total = totalResult.recordset[0]?.count || 0;
|
|
3001
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1582
3002
|
if (total === 0) {
|
|
1583
3003
|
return {
|
|
1584
3004
|
pagination: {
|
|
1585
3005
|
total: 0,
|
|
1586
|
-
page
|
|
1587
|
-
perPage:
|
|
3006
|
+
page,
|
|
3007
|
+
perPage: perPageInput,
|
|
1588
3008
|
hasMore: false
|
|
1589
3009
|
},
|
|
1590
3010
|
scores: []
|
|
1591
3011
|
};
|
|
1592
3012
|
}
|
|
3013
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
3014
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
3015
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
3016
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1593
3017
|
const dataRequest = this.pool.request();
|
|
1594
3018
|
dataRequest.input("p1", runId);
|
|
1595
|
-
dataRequest.input("p2",
|
|
1596
|
-
dataRequest.input("p3",
|
|
3019
|
+
dataRequest.input("p2", limitValue);
|
|
3020
|
+
dataRequest.input("p3", start);
|
|
1597
3021
|
const result = await dataRequest.query(
|
|
1598
|
-
`SELECT * FROM ${
|
|
3022
|
+
`SELECT * FROM ${getTableName2({ indexName: TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE [runId] = @p1 ORDER BY [createdAt] DESC OFFSET @p3 ROWS FETCH NEXT @p2 ROWS ONLY`
|
|
1599
3023
|
);
|
|
1600
3024
|
return {
|
|
1601
3025
|
pagination: {
|
|
1602
3026
|
total: Number(total),
|
|
1603
|
-
page
|
|
1604
|
-
perPage:
|
|
1605
|
-
hasMore:
|
|
3027
|
+
page,
|
|
3028
|
+
perPage: perPageForResponse,
|
|
3029
|
+
hasMore: end < total
|
|
1606
3030
|
},
|
|
1607
3031
|
scores: result.recordset.map((row) => transformScoreRow(row))
|
|
1608
3032
|
};
|
|
1609
3033
|
} catch (error) {
|
|
1610
3034
|
throw new MastraError(
|
|
1611
3035
|
{
|
|
1612
|
-
id: "
|
|
3036
|
+
id: createStorageErrorId("MSSQL", "LIST_SCORES_BY_RUN_ID", "FAILED"),
|
|
1613
3037
|
domain: ErrorDomain.STORAGE,
|
|
1614
3038
|
category: ErrorCategory.THIRD_PARTY,
|
|
1615
3039
|
details: { runId }
|
|
@@ -1618,7 +3042,7 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1618
3042
|
);
|
|
1619
3043
|
}
|
|
1620
3044
|
}
|
|
1621
|
-
async
|
|
3045
|
+
async listScoresByEntityId({
|
|
1622
3046
|
entityId,
|
|
1623
3047
|
entityType,
|
|
1624
3048
|
pagination
|
|
@@ -1628,41 +3052,46 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1628
3052
|
request.input("p1", entityId);
|
|
1629
3053
|
request.input("p2", entityType);
|
|
1630
3054
|
const totalResult = await request.query(
|
|
1631
|
-
`SELECT COUNT(*) as count FROM ${
|
|
3055
|
+
`SELECT COUNT(*) as count FROM ${getTableName2({ indexName: TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE [entityId] = @p1 AND [entityType] = @p2`
|
|
1632
3056
|
);
|
|
1633
3057
|
const total = totalResult.recordset[0]?.count || 0;
|
|
3058
|
+
const { page, perPage: perPageInput } = pagination;
|
|
3059
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
3060
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1634
3061
|
if (total === 0) {
|
|
1635
3062
|
return {
|
|
1636
3063
|
pagination: {
|
|
1637
3064
|
total: 0,
|
|
1638
|
-
page
|
|
1639
|
-
perPage:
|
|
3065
|
+
page,
|
|
3066
|
+
perPage: perPageForResponse,
|
|
1640
3067
|
hasMore: false
|
|
1641
3068
|
},
|
|
1642
3069
|
scores: []
|
|
1643
3070
|
};
|
|
1644
3071
|
}
|
|
3072
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
3073
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1645
3074
|
const dataRequest = this.pool.request();
|
|
1646
3075
|
dataRequest.input("p1", entityId);
|
|
1647
3076
|
dataRequest.input("p2", entityType);
|
|
1648
|
-
dataRequest.input("p3",
|
|
1649
|
-
dataRequest.input("p4",
|
|
3077
|
+
dataRequest.input("p3", limitValue);
|
|
3078
|
+
dataRequest.input("p4", start);
|
|
1650
3079
|
const result = await dataRequest.query(
|
|
1651
|
-
`SELECT * FROM ${
|
|
3080
|
+
`SELECT * FROM ${getTableName2({ indexName: TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE [entityId] = @p1 AND [entityType] = @p2 ORDER BY [createdAt] DESC OFFSET @p4 ROWS FETCH NEXT @p3 ROWS ONLY`
|
|
1652
3081
|
);
|
|
1653
3082
|
return {
|
|
1654
3083
|
pagination: {
|
|
1655
3084
|
total: Number(total),
|
|
1656
|
-
page
|
|
1657
|
-
perPage:
|
|
1658
|
-
hasMore:
|
|
3085
|
+
page,
|
|
3086
|
+
perPage: perPageForResponse,
|
|
3087
|
+
hasMore: end < total
|
|
1659
3088
|
},
|
|
1660
3089
|
scores: result.recordset.map((row) => transformScoreRow(row))
|
|
1661
3090
|
};
|
|
1662
3091
|
} catch (error) {
|
|
1663
3092
|
throw new MastraError(
|
|
1664
3093
|
{
|
|
1665
|
-
id: "
|
|
3094
|
+
id: createStorageErrorId("MSSQL", "LIST_SCORES_BY_ENTITY_ID", "FAILED"),
|
|
1666
3095
|
domain: ErrorDomain.STORAGE,
|
|
1667
3096
|
category: ErrorCategory.THIRD_PARTY,
|
|
1668
3097
|
details: { entityId, entityType }
|
|
@@ -1671,249 +3100,317 @@ var ScoresMSSQL = class extends ScoresStorage {
|
|
|
1671
3100
|
);
|
|
1672
3101
|
}
|
|
1673
3102
|
}
|
|
3103
|
+
async listScoresBySpan({
|
|
3104
|
+
traceId,
|
|
3105
|
+
spanId,
|
|
3106
|
+
pagination
|
|
3107
|
+
}) {
|
|
3108
|
+
try {
|
|
3109
|
+
const request = this.pool.request();
|
|
3110
|
+
request.input("p1", traceId);
|
|
3111
|
+
request.input("p2", spanId);
|
|
3112
|
+
const totalResult = await request.query(
|
|
3113
|
+
`SELECT COUNT(*) as count FROM ${getTableName2({ indexName: TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE [traceId] = @p1 AND [spanId] = @p2`
|
|
3114
|
+
);
|
|
3115
|
+
const total = totalResult.recordset[0]?.count || 0;
|
|
3116
|
+
const { page, perPage: perPageInput } = pagination;
|
|
3117
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
3118
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
3119
|
+
if (total === 0) {
|
|
3120
|
+
return {
|
|
3121
|
+
pagination: {
|
|
3122
|
+
total: 0,
|
|
3123
|
+
page,
|
|
3124
|
+
perPage: perPageForResponse,
|
|
3125
|
+
hasMore: false
|
|
3126
|
+
},
|
|
3127
|
+
scores: []
|
|
3128
|
+
};
|
|
3129
|
+
}
|
|
3130
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
3131
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
3132
|
+
const dataRequest = this.pool.request();
|
|
3133
|
+
dataRequest.input("p1", traceId);
|
|
3134
|
+
dataRequest.input("p2", spanId);
|
|
3135
|
+
dataRequest.input("p3", limitValue);
|
|
3136
|
+
dataRequest.input("p4", start);
|
|
3137
|
+
const result = await dataRequest.query(
|
|
3138
|
+
`SELECT * FROM ${getTableName2({ indexName: TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE [traceId] = @p1 AND [spanId] = @p2 ORDER BY [createdAt] DESC OFFSET @p4 ROWS FETCH NEXT @p3 ROWS ONLY`
|
|
3139
|
+
);
|
|
3140
|
+
return {
|
|
3141
|
+
pagination: {
|
|
3142
|
+
total: Number(total),
|
|
3143
|
+
page,
|
|
3144
|
+
perPage: perPageForResponse,
|
|
3145
|
+
hasMore: end < total
|
|
3146
|
+
},
|
|
3147
|
+
scores: result.recordset.map((row) => transformScoreRow(row))
|
|
3148
|
+
};
|
|
3149
|
+
} catch (error) {
|
|
3150
|
+
throw new MastraError(
|
|
3151
|
+
{
|
|
3152
|
+
id: createStorageErrorId("MSSQL", "LIST_SCORES_BY_SPAN", "FAILED"),
|
|
3153
|
+
domain: ErrorDomain.STORAGE,
|
|
3154
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
3155
|
+
details: { traceId, spanId }
|
|
3156
|
+
},
|
|
3157
|
+
error
|
|
3158
|
+
);
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
1674
3161
|
};
|
|
1675
|
-
var
|
|
3162
|
+
var WorkflowsMSSQL = class _WorkflowsMSSQL extends WorkflowsStorage {
|
|
1676
3163
|
pool;
|
|
1677
|
-
|
|
3164
|
+
db;
|
|
1678
3165
|
schema;
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
3166
|
+
needsConnect;
|
|
3167
|
+
skipDefaultIndexes;
|
|
3168
|
+
indexes;
|
|
3169
|
+
/** Tables managed by this domain */
|
|
3170
|
+
static MANAGED_TABLES = [TABLE_WORKFLOW_SNAPSHOT];
|
|
3171
|
+
constructor(config) {
|
|
1684
3172
|
super();
|
|
3173
|
+
const { pool, schemaName, skipDefaultIndexes, indexes, needsConnect } = resolveMssqlConfig(config);
|
|
1685
3174
|
this.pool = pool;
|
|
1686
|
-
this.
|
|
1687
|
-
this.
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
const paramMap = {};
|
|
1707
|
-
const conditions = [];
|
|
1708
|
-
let paramIndex = 1;
|
|
1709
|
-
if (name) {
|
|
1710
|
-
const paramName = `p${paramIndex++}`;
|
|
1711
|
-
conditions.push(`[name] LIKE @${paramName}`);
|
|
1712
|
-
paramMap[paramName] = `${name}%`;
|
|
1713
|
-
}
|
|
1714
|
-
if (scope) {
|
|
1715
|
-
const paramName = `p${paramIndex++}`;
|
|
1716
|
-
conditions.push(`[scope] = @${paramName}`);
|
|
1717
|
-
paramMap[paramName] = scope;
|
|
3175
|
+
this.schema = schemaName;
|
|
3176
|
+
this.db = new MssqlDB({ pool, schemaName, skipDefaultIndexes });
|
|
3177
|
+
this.needsConnect = needsConnect;
|
|
3178
|
+
this.skipDefaultIndexes = skipDefaultIndexes;
|
|
3179
|
+
this.indexes = indexes?.filter((idx) => _WorkflowsMSSQL.MANAGED_TABLES.includes(idx.table));
|
|
3180
|
+
}
|
|
3181
|
+
/**
|
|
3182
|
+
* Returns default index definitions for the workflows domain tables.
|
|
3183
|
+
* Currently no default indexes are defined for workflows.
|
|
3184
|
+
*/
|
|
3185
|
+
getDefaultIndexDefinitions() {
|
|
3186
|
+
return [];
|
|
3187
|
+
}
|
|
3188
|
+
/**
|
|
3189
|
+
* Creates default indexes for optimal query performance.
|
|
3190
|
+
* Currently no default indexes are defined for workflows.
|
|
3191
|
+
*/
|
|
3192
|
+
async createDefaultIndexes() {
|
|
3193
|
+
if (this.skipDefaultIndexes) {
|
|
3194
|
+
return;
|
|
1718
3195
|
}
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
paramMap[paramName] = value;
|
|
1725
|
-
});
|
|
3196
|
+
}
|
|
3197
|
+
async init() {
|
|
3198
|
+
if (this.needsConnect) {
|
|
3199
|
+
await this.pool.connect();
|
|
3200
|
+
this.needsConnect = false;
|
|
1726
3201
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
3202
|
+
await this.db.createTable({ tableName: TABLE_WORKFLOW_SNAPSHOT, schema: TABLE_SCHEMAS[TABLE_WORKFLOW_SNAPSHOT] });
|
|
3203
|
+
await this.createDefaultIndexes();
|
|
3204
|
+
await this.createCustomIndexes();
|
|
3205
|
+
}
|
|
3206
|
+
/**
|
|
3207
|
+
* Creates custom user-defined indexes for this domain's tables.
|
|
3208
|
+
*/
|
|
3209
|
+
async createCustomIndexes() {
|
|
3210
|
+
if (!this.indexes || this.indexes.length === 0) {
|
|
3211
|
+
return;
|
|
1734
3212
|
}
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
3213
|
+
for (const indexDef of this.indexes) {
|
|
3214
|
+
try {
|
|
3215
|
+
await this.db.createIndex(indexDef);
|
|
3216
|
+
} catch (error) {
|
|
3217
|
+
this.logger?.warn?.(`Failed to create custom index ${indexDef.name}:`, error);
|
|
3218
|
+
}
|
|
1739
3219
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
3220
|
+
}
|
|
3221
|
+
async dangerouslyClearAll() {
|
|
3222
|
+
await this.db.clearTable({ tableName: TABLE_WORKFLOW_SNAPSHOT });
|
|
3223
|
+
}
|
|
3224
|
+
parseWorkflowRun(row) {
|
|
3225
|
+
let parsedSnapshot = row.snapshot;
|
|
3226
|
+
if (typeof parsedSnapshot === "string") {
|
|
3227
|
+
try {
|
|
3228
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
3229
|
+
} catch (e) {
|
|
3230
|
+
this.logger?.warn?.(`Failed to parse snapshot for workflow ${row.workflow_name}:`, e);
|
|
3231
|
+
}
|
|
1744
3232
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
3233
|
+
return {
|
|
3234
|
+
workflowName: row.workflow_name,
|
|
3235
|
+
runId: row.run_id,
|
|
3236
|
+
snapshot: parsedSnapshot,
|
|
3237
|
+
createdAt: row.createdAt,
|
|
3238
|
+
updatedAt: row.updatedAt,
|
|
3239
|
+
resourceId: row.resourceId
|
|
3240
|
+
};
|
|
3241
|
+
}
|
|
3242
|
+
async updateWorkflowResults({
|
|
3243
|
+
workflowName,
|
|
3244
|
+
runId,
|
|
3245
|
+
stepId,
|
|
3246
|
+
result,
|
|
3247
|
+
requestContext
|
|
3248
|
+
}) {
|
|
3249
|
+
const table = getTableName2({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.schema) });
|
|
3250
|
+
const transaction = this.pool.transaction();
|
|
1748
3251
|
try {
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
3252
|
+
await transaction.begin();
|
|
3253
|
+
const selectRequest = new sql.Request(transaction);
|
|
3254
|
+
selectRequest.input("workflow_name", workflowName);
|
|
3255
|
+
selectRequest.input("run_id", runId);
|
|
3256
|
+
const existingSnapshotResult = await selectRequest.query(
|
|
3257
|
+
`SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
3258
|
+
);
|
|
3259
|
+
let snapshot;
|
|
3260
|
+
if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
|
|
3261
|
+
snapshot = {
|
|
3262
|
+
context: {},
|
|
3263
|
+
activePaths: [],
|
|
3264
|
+
activeStepsPath: {},
|
|
3265
|
+
timestamp: Date.now(),
|
|
3266
|
+
suspendedPaths: {},
|
|
3267
|
+
resumeLabels: {},
|
|
3268
|
+
serializedStepGraph: [],
|
|
3269
|
+
status: "pending",
|
|
3270
|
+
value: {},
|
|
3271
|
+
waitingPaths: {},
|
|
3272
|
+
runId,
|
|
3273
|
+
requestContext: {}
|
|
3274
|
+
};
|
|
3275
|
+
} else {
|
|
3276
|
+
const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
|
|
3277
|
+
snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
|
|
3278
|
+
}
|
|
3279
|
+
snapshot.context[stepId] = result;
|
|
3280
|
+
snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
|
|
3281
|
+
const upsertReq = new sql.Request(transaction);
|
|
3282
|
+
upsertReq.input("workflow_name", workflowName);
|
|
3283
|
+
upsertReq.input("run_id", runId);
|
|
3284
|
+
upsertReq.input("snapshot", JSON.stringify(snapshot));
|
|
3285
|
+
upsertReq.input("createdAt", sql.DateTime2, /* @__PURE__ */ new Date());
|
|
3286
|
+
upsertReq.input("updatedAt", sql.DateTime2, /* @__PURE__ */ new Date());
|
|
3287
|
+
await upsertReq.query(
|
|
3288
|
+
`MERGE ${table} AS target
|
|
3289
|
+
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
3290
|
+
ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
|
|
3291
|
+
WHEN MATCHED THEN UPDATE SET snapshot = @snapshot, [updatedAt] = @updatedAt
|
|
3292
|
+
WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
|
|
3293
|
+
VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`
|
|
3294
|
+
);
|
|
3295
|
+
await transaction.commit();
|
|
3296
|
+
return snapshot.context;
|
|
1759
3297
|
} catch (error) {
|
|
3298
|
+
try {
|
|
3299
|
+
await transaction.rollback();
|
|
3300
|
+
} catch {
|
|
3301
|
+
}
|
|
1760
3302
|
throw new MastraError(
|
|
1761
3303
|
{
|
|
1762
|
-
id: "
|
|
3304
|
+
id: createStorageErrorId("MSSQL", "UPDATE_WORKFLOW_RESULTS", "FAILED"),
|
|
1763
3305
|
domain: ErrorDomain.STORAGE,
|
|
1764
3306
|
category: ErrorCategory.THIRD_PARTY,
|
|
1765
3307
|
details: {
|
|
1766
|
-
|
|
1767
|
-
|
|
3308
|
+
workflowName,
|
|
3309
|
+
runId,
|
|
3310
|
+
stepId
|
|
1768
3311
|
}
|
|
1769
3312
|
},
|
|
1770
3313
|
error
|
|
1771
3314
|
);
|
|
1772
3315
|
}
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
}
|
|
1782
|
-
const dataQuery = `SELECT * FROM ${getTableName({ indexName: TABLE_TRACES, schemaName: getSchemaName(this.schema) })} ${whereClause} ORDER BY [seq_id] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`;
|
|
1783
|
-
const dataRequest = this.pool.request();
|
|
1784
|
-
Object.entries(paramMap).forEach(([key, value]) => {
|
|
1785
|
-
if (value instanceof Date) {
|
|
1786
|
-
dataRequest.input(key, sql2.DateTime, value);
|
|
1787
|
-
} else {
|
|
1788
|
-
dataRequest.input(key, value);
|
|
1789
|
-
}
|
|
1790
|
-
});
|
|
1791
|
-
dataRequest.input("offset", currentOffset);
|
|
1792
|
-
dataRequest.input("limit", perPage);
|
|
3316
|
+
}
|
|
3317
|
+
async updateWorkflowState({
|
|
3318
|
+
workflowName,
|
|
3319
|
+
runId,
|
|
3320
|
+
opts
|
|
3321
|
+
}) {
|
|
3322
|
+
const table = getTableName2({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.schema) });
|
|
3323
|
+
const transaction = this.pool.transaction();
|
|
1793
3324
|
try {
|
|
1794
|
-
|
|
1795
|
-
const
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
3325
|
+
await transaction.begin();
|
|
3326
|
+
const selectRequest = new sql.Request(transaction);
|
|
3327
|
+
selectRequest.input("workflow_name", workflowName);
|
|
3328
|
+
selectRequest.input("run_id", runId);
|
|
3329
|
+
const existingSnapshotResult = await selectRequest.query(
|
|
3330
|
+
`SELECT snapshot FROM ${table} WITH (UPDLOCK, HOLDLOCK) WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
3331
|
+
);
|
|
3332
|
+
if (!existingSnapshotResult.recordset || existingSnapshotResult.recordset.length === 0) {
|
|
3333
|
+
await transaction.rollback();
|
|
3334
|
+
return void 0;
|
|
3335
|
+
}
|
|
3336
|
+
const existingSnapshot = existingSnapshotResult.recordset[0].snapshot;
|
|
3337
|
+
const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
|
|
3338
|
+
if (!snapshot || !snapshot?.context) {
|
|
3339
|
+
await transaction.rollback();
|
|
3340
|
+
throw new MastraError(
|
|
3341
|
+
{
|
|
3342
|
+
id: createStorageErrorId("MSSQL", "UPDATE_WORKFLOW_STATE", "SNAPSHOT_NOT_FOUND"),
|
|
3343
|
+
domain: ErrorDomain.STORAGE,
|
|
3344
|
+
category: ErrorCategory.SYSTEM,
|
|
3345
|
+
details: {
|
|
3346
|
+
workflowName,
|
|
3347
|
+
runId
|
|
3348
|
+
}
|
|
3349
|
+
},
|
|
3350
|
+
new Error(`Snapshot not found for runId ${runId}`)
|
|
3351
|
+
);
|
|
3352
|
+
}
|
|
3353
|
+
const updatedSnapshot = { ...snapshot, ...opts };
|
|
3354
|
+
const updateRequest = new sql.Request(transaction);
|
|
3355
|
+
updateRequest.input("snapshot", JSON.stringify(updatedSnapshot));
|
|
3356
|
+
updateRequest.input("workflow_name", workflowName);
|
|
3357
|
+
updateRequest.input("run_id", runId);
|
|
3358
|
+
updateRequest.input("updatedAt", sql.DateTime2, /* @__PURE__ */ new Date());
|
|
3359
|
+
await updateRequest.query(
|
|
3360
|
+
`UPDATE ${table} SET snapshot = @snapshot, [updatedAt] = @updatedAt WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
3361
|
+
);
|
|
3362
|
+
await transaction.commit();
|
|
3363
|
+
return updatedSnapshot;
|
|
1819
3364
|
} catch (error) {
|
|
3365
|
+
try {
|
|
3366
|
+
await transaction.rollback();
|
|
3367
|
+
} catch {
|
|
3368
|
+
}
|
|
3369
|
+
if (error instanceof MastraError) throw error;
|
|
1820
3370
|
throw new MastraError(
|
|
1821
3371
|
{
|
|
1822
|
-
id: "
|
|
3372
|
+
id: createStorageErrorId("MSSQL", "UPDATE_WORKFLOW_STATE", "FAILED"),
|
|
1823
3373
|
domain: ErrorDomain.STORAGE,
|
|
1824
3374
|
category: ErrorCategory.THIRD_PARTY,
|
|
1825
3375
|
details: {
|
|
1826
|
-
|
|
1827
|
-
|
|
3376
|
+
workflowName,
|
|
3377
|
+
runId
|
|
1828
3378
|
}
|
|
1829
3379
|
},
|
|
1830
3380
|
error
|
|
1831
3381
|
);
|
|
1832
3382
|
}
|
|
1833
3383
|
}
|
|
1834
|
-
async batchTraceInsert({ records }) {
|
|
1835
|
-
this.logger.debug("Batch inserting traces", { count: records.length });
|
|
1836
|
-
await this.operations.batchInsert({
|
|
1837
|
-
tableName: TABLE_TRACES,
|
|
1838
|
-
records
|
|
1839
|
-
});
|
|
1840
|
-
}
|
|
1841
|
-
};
|
|
1842
|
-
function parseWorkflowRun(row) {
|
|
1843
|
-
let parsedSnapshot = row.snapshot;
|
|
1844
|
-
if (typeof parsedSnapshot === "string") {
|
|
1845
|
-
try {
|
|
1846
|
-
parsedSnapshot = JSON.parse(row.snapshot);
|
|
1847
|
-
} catch (e) {
|
|
1848
|
-
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1851
|
-
return {
|
|
1852
|
-
workflowName: row.workflow_name,
|
|
1853
|
-
runId: row.run_id,
|
|
1854
|
-
snapshot: parsedSnapshot,
|
|
1855
|
-
createdAt: row.createdAt,
|
|
1856
|
-
updatedAt: row.updatedAt,
|
|
1857
|
-
resourceId: row.resourceId
|
|
1858
|
-
};
|
|
1859
|
-
}
|
|
1860
|
-
var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
1861
|
-
pool;
|
|
1862
|
-
operations;
|
|
1863
|
-
schema;
|
|
1864
|
-
constructor({
|
|
1865
|
-
pool,
|
|
1866
|
-
operations,
|
|
1867
|
-
schema
|
|
1868
|
-
}) {
|
|
1869
|
-
super();
|
|
1870
|
-
this.pool = pool;
|
|
1871
|
-
this.operations = operations;
|
|
1872
|
-
this.schema = schema;
|
|
1873
|
-
}
|
|
1874
|
-
updateWorkflowResults({
|
|
1875
|
-
// workflowName,
|
|
1876
|
-
// runId,
|
|
1877
|
-
// stepId,
|
|
1878
|
-
// result,
|
|
1879
|
-
// runtimeContext,
|
|
1880
|
-
}) {
|
|
1881
|
-
throw new Error("Method not implemented.");
|
|
1882
|
-
}
|
|
1883
|
-
updateWorkflowState({
|
|
1884
|
-
// workflowName,
|
|
1885
|
-
// runId,
|
|
1886
|
-
// opts,
|
|
1887
|
-
}) {
|
|
1888
|
-
throw new Error("Method not implemented.");
|
|
1889
|
-
}
|
|
1890
3384
|
async persistWorkflowSnapshot({
|
|
1891
3385
|
workflowName,
|
|
1892
3386
|
runId,
|
|
3387
|
+
resourceId,
|
|
1893
3388
|
snapshot
|
|
1894
3389
|
}) {
|
|
1895
|
-
const table =
|
|
3390
|
+
const table = getTableName2({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.schema) });
|
|
1896
3391
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1897
3392
|
try {
|
|
1898
3393
|
const request = this.pool.request();
|
|
1899
3394
|
request.input("workflow_name", workflowName);
|
|
1900
3395
|
request.input("run_id", runId);
|
|
3396
|
+
request.input("resourceId", resourceId);
|
|
1901
3397
|
request.input("snapshot", JSON.stringify(snapshot));
|
|
1902
|
-
request.input("createdAt",
|
|
1903
|
-
request.input("updatedAt",
|
|
3398
|
+
request.input("createdAt", sql.DateTime2, new Date(now));
|
|
3399
|
+
request.input("updatedAt", sql.DateTime2, new Date(now));
|
|
1904
3400
|
const mergeSql = `MERGE INTO ${table} AS target
|
|
1905
3401
|
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
1906
3402
|
ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
|
|
1907
3403
|
WHEN MATCHED THEN UPDATE SET
|
|
3404
|
+
resourceId = @resourceId,
|
|
1908
3405
|
snapshot = @snapshot,
|
|
1909
3406
|
[updatedAt] = @updatedAt
|
|
1910
|
-
WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, snapshot, [createdAt], [updatedAt])
|
|
1911
|
-
VALUES (@workflow_name, @run_id, @snapshot, @createdAt, @updatedAt);`;
|
|
3407
|
+
WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, resourceId, snapshot, [createdAt], [updatedAt])
|
|
3408
|
+
VALUES (@workflow_name, @run_id, @resourceId, @snapshot, @createdAt, @updatedAt);`;
|
|
1912
3409
|
await request.query(mergeSql);
|
|
1913
3410
|
} catch (error) {
|
|
1914
3411
|
throw new MastraError(
|
|
1915
3412
|
{
|
|
1916
|
-
id: "
|
|
3413
|
+
id: createStorageErrorId("MSSQL", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
|
|
1917
3414
|
domain: ErrorDomain.STORAGE,
|
|
1918
3415
|
category: ErrorCategory.THIRD_PARTY,
|
|
1919
3416
|
details: {
|
|
@@ -1930,7 +3427,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1930
3427
|
runId
|
|
1931
3428
|
}) {
|
|
1932
3429
|
try {
|
|
1933
|
-
const result = await this.
|
|
3430
|
+
const result = await this.db.load({
|
|
1934
3431
|
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
1935
3432
|
keys: {
|
|
1936
3433
|
workflow_name: workflowName,
|
|
@@ -1944,7 +3441,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1944
3441
|
} catch (error) {
|
|
1945
3442
|
throw new MastraError(
|
|
1946
3443
|
{
|
|
1947
|
-
id: "
|
|
3444
|
+
id: createStorageErrorId("MSSQL", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
|
|
1948
3445
|
domain: ErrorDomain.STORAGE,
|
|
1949
3446
|
category: ErrorCategory.THIRD_PARTY,
|
|
1950
3447
|
details: {
|
|
@@ -1972,7 +3469,7 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1972
3469
|
paramMap["workflowName"] = workflowName;
|
|
1973
3470
|
}
|
|
1974
3471
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1975
|
-
const tableName =
|
|
3472
|
+
const tableName = getTableName2({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.schema) });
|
|
1976
3473
|
const query = `SELECT * FROM ${tableName} ${whereClause}`;
|
|
1977
3474
|
const request = this.pool.request();
|
|
1978
3475
|
Object.entries(paramMap).forEach(([key, value]) => request.input(key, value));
|
|
@@ -1980,11 +3477,11 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1980
3477
|
if (!result.recordset || result.recordset.length === 0) {
|
|
1981
3478
|
return null;
|
|
1982
3479
|
}
|
|
1983
|
-
return parseWorkflowRun(result.recordset[0]);
|
|
3480
|
+
return this.parseWorkflowRun(result.recordset[0]);
|
|
1984
3481
|
} catch (error) {
|
|
1985
3482
|
throw new MastraError(
|
|
1986
3483
|
{
|
|
1987
|
-
id: "
|
|
3484
|
+
id: createStorageErrorId("MSSQL", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
1988
3485
|
domain: ErrorDomain.STORAGE,
|
|
1989
3486
|
category: ErrorCategory.THIRD_PARTY,
|
|
1990
3487
|
details: {
|
|
@@ -1996,13 +3493,43 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
1996
3493
|
);
|
|
1997
3494
|
}
|
|
1998
3495
|
}
|
|
1999
|
-
async
|
|
3496
|
+
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
3497
|
+
const table = getTableName2({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.schema) });
|
|
3498
|
+
const transaction = this.pool.transaction();
|
|
3499
|
+
try {
|
|
3500
|
+
await transaction.begin();
|
|
3501
|
+
const deleteRequest = new sql.Request(transaction);
|
|
3502
|
+
deleteRequest.input("workflow_name", workflowName);
|
|
3503
|
+
deleteRequest.input("run_id", runId);
|
|
3504
|
+
await deleteRequest.query(`DELETE FROM ${table} WHERE workflow_name = @workflow_name AND run_id = @run_id`);
|
|
3505
|
+
await transaction.commit();
|
|
3506
|
+
} catch (error) {
|
|
3507
|
+
try {
|
|
3508
|
+
await transaction.rollback();
|
|
3509
|
+
} catch {
|
|
3510
|
+
}
|
|
3511
|
+
throw new MastraError(
|
|
3512
|
+
{
|
|
3513
|
+
id: createStorageErrorId("MSSQL", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
3514
|
+
domain: ErrorDomain.STORAGE,
|
|
3515
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
3516
|
+
details: {
|
|
3517
|
+
runId,
|
|
3518
|
+
workflowName
|
|
3519
|
+
}
|
|
3520
|
+
},
|
|
3521
|
+
error
|
|
3522
|
+
);
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
async listWorkflowRuns({
|
|
2000
3526
|
workflowName,
|
|
2001
3527
|
fromDate,
|
|
2002
3528
|
toDate,
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
resourceId
|
|
3529
|
+
page,
|
|
3530
|
+
perPage,
|
|
3531
|
+
resourceId,
|
|
3532
|
+
status
|
|
2006
3533
|
} = {}) {
|
|
2007
3534
|
try {
|
|
2008
3535
|
const conditions = [];
|
|
@@ -2011,13 +3538,17 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2011
3538
|
conditions.push(`[workflow_name] = @workflowName`);
|
|
2012
3539
|
paramMap["workflowName"] = workflowName;
|
|
2013
3540
|
}
|
|
3541
|
+
if (status) {
|
|
3542
|
+
conditions.push(`JSON_VALUE([snapshot], '$.status') = @status`);
|
|
3543
|
+
paramMap["status"] = status;
|
|
3544
|
+
}
|
|
2014
3545
|
if (resourceId) {
|
|
2015
|
-
const hasResourceId = await this.
|
|
3546
|
+
const hasResourceId = await this.db.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
|
|
2016
3547
|
if (hasResourceId) {
|
|
2017
3548
|
conditions.push(`[resourceId] = @resourceId`);
|
|
2018
3549
|
paramMap["resourceId"] = resourceId;
|
|
2019
3550
|
} else {
|
|
2020
|
-
|
|
3551
|
+
this.logger?.warn?.(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
2021
3552
|
}
|
|
2022
3553
|
}
|
|
2023
3554
|
if (fromDate instanceof Date && !isNaN(fromDate.getTime())) {
|
|
@@ -2030,33 +3561,36 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2030
3561
|
}
|
|
2031
3562
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2032
3563
|
let total = 0;
|
|
2033
|
-
const tableName =
|
|
3564
|
+
const tableName = getTableName2({ indexName: TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.schema) });
|
|
2034
3565
|
const request = this.pool.request();
|
|
2035
3566
|
Object.entries(paramMap).forEach(([key, value]) => {
|
|
2036
3567
|
if (value instanceof Date) {
|
|
2037
|
-
request.input(key,
|
|
3568
|
+
request.input(key, sql.DateTime, value);
|
|
2038
3569
|
} else {
|
|
2039
3570
|
request.input(key, value);
|
|
2040
3571
|
}
|
|
2041
3572
|
});
|
|
2042
|
-
|
|
3573
|
+
const usePagination = typeof perPage === "number" && typeof page === "number";
|
|
3574
|
+
if (usePagination) {
|
|
2043
3575
|
const countQuery = `SELECT COUNT(*) as count FROM ${tableName} ${whereClause}`;
|
|
2044
3576
|
const countResult = await request.query(countQuery);
|
|
2045
3577
|
total = Number(countResult.recordset[0]?.count || 0);
|
|
2046
3578
|
}
|
|
2047
3579
|
let query = `SELECT * FROM ${tableName} ${whereClause} ORDER BY [seq_id] DESC`;
|
|
2048
|
-
if (
|
|
2049
|
-
|
|
2050
|
-
|
|
3580
|
+
if (usePagination) {
|
|
3581
|
+
const normalizedPerPage = normalizePerPage(perPage, Number.MAX_SAFE_INTEGER);
|
|
3582
|
+
const offset = page * normalizedPerPage;
|
|
3583
|
+
query += ` OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
3584
|
+
request.input("perPage", normalizedPerPage);
|
|
2051
3585
|
request.input("offset", offset);
|
|
2052
3586
|
}
|
|
2053
3587
|
const result = await request.query(query);
|
|
2054
|
-
const runs = (result.recordset || []).map((row) => parseWorkflowRun(row));
|
|
3588
|
+
const runs = (result.recordset || []).map((row) => this.parseWorkflowRun(row));
|
|
2055
3589
|
return { runs, total: total || runs.length };
|
|
2056
3590
|
} catch (error) {
|
|
2057
3591
|
throw new MastraError(
|
|
2058
3592
|
{
|
|
2059
|
-
id: "
|
|
3593
|
+
id: createStorageErrorId("MSSQL", "LIST_WORKFLOW_RUNS", "FAILED"),
|
|
2060
3594
|
domain: ErrorDomain.STORAGE,
|
|
2061
3595
|
category: ErrorCategory.THIRD_PARTY,
|
|
2062
3596
|
details: {
|
|
@@ -2070,18 +3604,28 @@ var WorkflowsMSSQL = class extends WorkflowsStorage {
|
|
|
2070
3604
|
};
|
|
2071
3605
|
|
|
2072
3606
|
// src/storage/index.ts
|
|
3607
|
+
var isPoolConfig = (config) => {
|
|
3608
|
+
return "pool" in config;
|
|
3609
|
+
};
|
|
2073
3610
|
var MSSQLStore = class extends MastraStorage {
|
|
2074
3611
|
pool;
|
|
2075
3612
|
schema;
|
|
2076
3613
|
isConnected = null;
|
|
2077
3614
|
stores;
|
|
2078
3615
|
constructor(config) {
|
|
2079
|
-
|
|
3616
|
+
if (!config.id || typeof config.id !== "string" || config.id.trim() === "") {
|
|
3617
|
+
throw new Error("MSSQLStore: id must be provided and cannot be empty.");
|
|
3618
|
+
}
|
|
3619
|
+
super({ id: config.id, name: "MSSQLStore", disableInit: config.disableInit });
|
|
2080
3620
|
try {
|
|
2081
|
-
|
|
3621
|
+
this.schema = config.schemaName || "dbo";
|
|
3622
|
+
if (isPoolConfig(config)) {
|
|
3623
|
+
this.pool = config.pool;
|
|
3624
|
+
} else if ("connectionString" in config) {
|
|
2082
3625
|
if (!config.connectionString || typeof config.connectionString !== "string" || config.connectionString.trim() === "") {
|
|
2083
3626
|
throw new Error("MSSQLStore: connectionString must be provided and cannot be empty.");
|
|
2084
3627
|
}
|
|
3628
|
+
this.pool = new sql.ConnectionPool(config.connectionString);
|
|
2085
3629
|
} else {
|
|
2086
3630
|
const required = ["server", "database", "user", "password"];
|
|
2087
3631
|
for (const key of required) {
|
|
@@ -2089,34 +3633,35 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2089
3633
|
throw new Error(`MSSQLStore: ${key} must be provided and cannot be empty.`);
|
|
2090
3634
|
}
|
|
2091
3635
|
}
|
|
3636
|
+
this.pool = new sql.ConnectionPool({
|
|
3637
|
+
server: config.server,
|
|
3638
|
+
database: config.database,
|
|
3639
|
+
user: config.user,
|
|
3640
|
+
password: config.password,
|
|
3641
|
+
port: config.port,
|
|
3642
|
+
options: config.options || { encrypt: true, trustServerCertificate: true }
|
|
3643
|
+
});
|
|
2092
3644
|
}
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
const
|
|
2103
|
-
const operations = new StoreOperationsMSSQL({ pool: this.pool, schemaName: this.schema });
|
|
2104
|
-
const scores = new ScoresMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2105
|
-
const traces = new TracesMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2106
|
-
const workflows = new WorkflowsMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
2107
|
-
const memory = new MemoryMSSQL({ pool: this.pool, schema: this.schema, operations });
|
|
3645
|
+
const domainConfig = {
|
|
3646
|
+
pool: this.pool,
|
|
3647
|
+
schemaName: this.schema,
|
|
3648
|
+
skipDefaultIndexes: config.skipDefaultIndexes,
|
|
3649
|
+
indexes: config.indexes
|
|
3650
|
+
};
|
|
3651
|
+
const scores = new ScoresMSSQL(domainConfig);
|
|
3652
|
+
const workflows = new WorkflowsMSSQL(domainConfig);
|
|
3653
|
+
const memory = new MemoryMSSQL(domainConfig);
|
|
3654
|
+
const observability = new ObservabilityMSSQL(domainConfig);
|
|
2108
3655
|
this.stores = {
|
|
2109
|
-
operations,
|
|
2110
3656
|
scores,
|
|
2111
|
-
traces,
|
|
2112
3657
|
workflows,
|
|
2113
|
-
|
|
2114
|
-
|
|
3658
|
+
memory,
|
|
3659
|
+
observability
|
|
2115
3660
|
};
|
|
2116
3661
|
} catch (e) {
|
|
2117
3662
|
throw new MastraError(
|
|
2118
3663
|
{
|
|
2119
|
-
id: "
|
|
3664
|
+
id: createStorageErrorId("MSSQL", "INITIALIZATION", "FAILED"),
|
|
2120
3665
|
domain: ErrorDomain.STORAGE,
|
|
2121
3666
|
category: ErrorCategory.USER
|
|
2122
3667
|
},
|
|
@@ -2135,7 +3680,7 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2135
3680
|
this.isConnected = null;
|
|
2136
3681
|
throw new MastraError(
|
|
2137
3682
|
{
|
|
2138
|
-
id: "
|
|
3683
|
+
id: createStorageErrorId("MSSQL", "INIT", "FAILED"),
|
|
2139
3684
|
domain: ErrorDomain.STORAGE,
|
|
2140
3685
|
category: ErrorCategory.THIRD_PARTY
|
|
2141
3686
|
},
|
|
@@ -2151,211 +3696,16 @@ var MSSQLStore = class extends MastraStorage {
|
|
|
2151
3696
|
throw err;
|
|
2152
3697
|
}
|
|
2153
3698
|
}
|
|
2154
|
-
get supports() {
|
|
2155
|
-
return {
|
|
2156
|
-
selectByIncludeResourceScope: true,
|
|
2157
|
-
resourceWorkingMemory: true,
|
|
2158
|
-
hasColumn: true,
|
|
2159
|
-
createTable: true,
|
|
2160
|
-
deleteMessages: true
|
|
2161
|
-
};
|
|
2162
|
-
}
|
|
2163
|
-
/** @deprecated use getEvals instead */
|
|
2164
|
-
async getEvalsByAgentName(agentName, type) {
|
|
2165
|
-
return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
|
|
2166
|
-
}
|
|
2167
|
-
async getEvals(options = {}) {
|
|
2168
|
-
return this.stores.legacyEvals.getEvals(options);
|
|
2169
|
-
}
|
|
2170
|
-
/**
|
|
2171
|
-
* @deprecated use getTracesPaginated instead
|
|
2172
|
-
*/
|
|
2173
|
-
async getTraces(args) {
|
|
2174
|
-
return this.stores.traces.getTraces(args);
|
|
2175
|
-
}
|
|
2176
|
-
async getTracesPaginated(args) {
|
|
2177
|
-
return this.stores.traces.getTracesPaginated(args);
|
|
2178
|
-
}
|
|
2179
|
-
async batchTraceInsert({ records }) {
|
|
2180
|
-
return this.stores.traces.batchTraceInsert({ records });
|
|
2181
|
-
}
|
|
2182
|
-
async createTable({
|
|
2183
|
-
tableName,
|
|
2184
|
-
schema
|
|
2185
|
-
}) {
|
|
2186
|
-
return this.stores.operations.createTable({ tableName, schema });
|
|
2187
|
-
}
|
|
2188
|
-
async alterTable({
|
|
2189
|
-
tableName,
|
|
2190
|
-
schema,
|
|
2191
|
-
ifNotExists
|
|
2192
|
-
}) {
|
|
2193
|
-
return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
|
|
2194
|
-
}
|
|
2195
|
-
async clearTable({ tableName }) {
|
|
2196
|
-
return this.stores.operations.clearTable({ tableName });
|
|
2197
|
-
}
|
|
2198
|
-
async dropTable({ tableName }) {
|
|
2199
|
-
return this.stores.operations.dropTable({ tableName });
|
|
2200
|
-
}
|
|
2201
|
-
async insert({ tableName, record }) {
|
|
2202
|
-
return this.stores.operations.insert({ tableName, record });
|
|
2203
|
-
}
|
|
2204
|
-
async batchInsert({ tableName, records }) {
|
|
2205
|
-
return this.stores.operations.batchInsert({ tableName, records });
|
|
2206
|
-
}
|
|
2207
|
-
async load({ tableName, keys }) {
|
|
2208
|
-
return this.stores.operations.load({ tableName, keys });
|
|
2209
|
-
}
|
|
2210
|
-
/**
|
|
2211
|
-
* Memory
|
|
2212
|
-
*/
|
|
2213
|
-
async getThreadById({ threadId }) {
|
|
2214
|
-
return this.stores.memory.getThreadById({ threadId });
|
|
2215
|
-
}
|
|
2216
|
-
/**
|
|
2217
|
-
* @deprecated use getThreadsByResourceIdPaginated instead
|
|
2218
|
-
*/
|
|
2219
|
-
async getThreadsByResourceId(args) {
|
|
2220
|
-
return this.stores.memory.getThreadsByResourceId(args);
|
|
2221
|
-
}
|
|
2222
|
-
async getThreadsByResourceIdPaginated(args) {
|
|
2223
|
-
return this.stores.memory.getThreadsByResourceIdPaginated(args);
|
|
2224
|
-
}
|
|
2225
|
-
async saveThread({ thread }) {
|
|
2226
|
-
return this.stores.memory.saveThread({ thread });
|
|
2227
|
-
}
|
|
2228
|
-
async updateThread({
|
|
2229
|
-
id,
|
|
2230
|
-
title,
|
|
2231
|
-
metadata
|
|
2232
|
-
}) {
|
|
2233
|
-
return this.stores.memory.updateThread({ id, title, metadata });
|
|
2234
|
-
}
|
|
2235
|
-
async deleteThread({ threadId }) {
|
|
2236
|
-
return this.stores.memory.deleteThread({ threadId });
|
|
2237
|
-
}
|
|
2238
|
-
async getMessages(args) {
|
|
2239
|
-
return this.stores.memory.getMessages(args);
|
|
2240
|
-
}
|
|
2241
|
-
async getMessagesById({
|
|
2242
|
-
messageIds,
|
|
2243
|
-
format
|
|
2244
|
-
}) {
|
|
2245
|
-
return this.stores.memory.getMessagesById({ messageIds, format });
|
|
2246
|
-
}
|
|
2247
|
-
async getMessagesPaginated(args) {
|
|
2248
|
-
return this.stores.memory.getMessagesPaginated(args);
|
|
2249
|
-
}
|
|
2250
|
-
async saveMessages(args) {
|
|
2251
|
-
return this.stores.memory.saveMessages(args);
|
|
2252
|
-
}
|
|
2253
|
-
async updateMessages({
|
|
2254
|
-
messages
|
|
2255
|
-
}) {
|
|
2256
|
-
return this.stores.memory.updateMessages({ messages });
|
|
2257
|
-
}
|
|
2258
|
-
async deleteMessages(messageIds) {
|
|
2259
|
-
return this.stores.memory.deleteMessages(messageIds);
|
|
2260
|
-
}
|
|
2261
|
-
async getResourceById({ resourceId }) {
|
|
2262
|
-
return this.stores.memory.getResourceById({ resourceId });
|
|
2263
|
-
}
|
|
2264
|
-
async saveResource({ resource }) {
|
|
2265
|
-
return this.stores.memory.saveResource({ resource });
|
|
2266
|
-
}
|
|
2267
|
-
async updateResource({
|
|
2268
|
-
resourceId,
|
|
2269
|
-
workingMemory,
|
|
2270
|
-
metadata
|
|
2271
|
-
}) {
|
|
2272
|
-
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
2273
|
-
}
|
|
2274
3699
|
/**
|
|
2275
|
-
*
|
|
3700
|
+
* Closes the MSSQL connection pool.
|
|
3701
|
+
*
|
|
3702
|
+
* This will close the connection pool, including pre-configured pools.
|
|
2276
3703
|
*/
|
|
2277
|
-
async updateWorkflowResults({
|
|
2278
|
-
workflowName,
|
|
2279
|
-
runId,
|
|
2280
|
-
stepId,
|
|
2281
|
-
result,
|
|
2282
|
-
runtimeContext
|
|
2283
|
-
}) {
|
|
2284
|
-
return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, runtimeContext });
|
|
2285
|
-
}
|
|
2286
|
-
async updateWorkflowState({
|
|
2287
|
-
workflowName,
|
|
2288
|
-
runId,
|
|
2289
|
-
opts
|
|
2290
|
-
}) {
|
|
2291
|
-
return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
|
|
2292
|
-
}
|
|
2293
|
-
async persistWorkflowSnapshot({
|
|
2294
|
-
workflowName,
|
|
2295
|
-
runId,
|
|
2296
|
-
snapshot
|
|
2297
|
-
}) {
|
|
2298
|
-
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
|
|
2299
|
-
}
|
|
2300
|
-
async loadWorkflowSnapshot({
|
|
2301
|
-
workflowName,
|
|
2302
|
-
runId
|
|
2303
|
-
}) {
|
|
2304
|
-
return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
|
|
2305
|
-
}
|
|
2306
|
-
async getWorkflowRuns({
|
|
2307
|
-
workflowName,
|
|
2308
|
-
fromDate,
|
|
2309
|
-
toDate,
|
|
2310
|
-
limit,
|
|
2311
|
-
offset,
|
|
2312
|
-
resourceId
|
|
2313
|
-
} = {}) {
|
|
2314
|
-
return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
|
|
2315
|
-
}
|
|
2316
|
-
async getWorkflowRunById({
|
|
2317
|
-
runId,
|
|
2318
|
-
workflowName
|
|
2319
|
-
}) {
|
|
2320
|
-
return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
|
|
2321
|
-
}
|
|
2322
3704
|
async close() {
|
|
2323
3705
|
await this.pool.close();
|
|
2324
3706
|
}
|
|
2325
|
-
/**
|
|
2326
|
-
* Scorers
|
|
2327
|
-
*/
|
|
2328
|
-
async getScoreById({ id: _id }) {
|
|
2329
|
-
return this.stores.scores.getScoreById({ id: _id });
|
|
2330
|
-
}
|
|
2331
|
-
async getScoresByScorerId({
|
|
2332
|
-
scorerId: _scorerId,
|
|
2333
|
-
pagination: _pagination
|
|
2334
|
-
}) {
|
|
2335
|
-
return this.stores.scores.getScoresByScorerId({ scorerId: _scorerId, pagination: _pagination });
|
|
2336
|
-
}
|
|
2337
|
-
async saveScore(_score) {
|
|
2338
|
-
return this.stores.scores.saveScore(_score);
|
|
2339
|
-
}
|
|
2340
|
-
async getScoresByRunId({
|
|
2341
|
-
runId: _runId,
|
|
2342
|
-
pagination: _pagination
|
|
2343
|
-
}) {
|
|
2344
|
-
return this.stores.scores.getScoresByRunId({ runId: _runId, pagination: _pagination });
|
|
2345
|
-
}
|
|
2346
|
-
async getScoresByEntityId({
|
|
2347
|
-
entityId: _entityId,
|
|
2348
|
-
entityType: _entityType,
|
|
2349
|
-
pagination: _pagination
|
|
2350
|
-
}) {
|
|
2351
|
-
return this.stores.scores.getScoresByEntityId({
|
|
2352
|
-
entityId: _entityId,
|
|
2353
|
-
entityType: _entityType,
|
|
2354
|
-
pagination: _pagination
|
|
2355
|
-
});
|
|
2356
|
-
}
|
|
2357
3707
|
};
|
|
2358
3708
|
|
|
2359
|
-
export { MSSQLStore };
|
|
3709
|
+
export { MSSQLStore, MemoryMSSQL, ObservabilityMSSQL, ScoresMSSQL, WorkflowsMSSQL };
|
|
2360
3710
|
//# sourceMappingURL=index.js.map
|
|
2361
3711
|
//# sourceMappingURL=index.js.map
|