@mastra/mssql 0.0.0-fix-prompt-enhance-route-20251210210827 → 0.0.0-fix-11329-windows-path-20251222155941
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 +175 -35
- package/dist/index.cjs +1975 -1929
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1975 -1929
- package/dist/index.js.map +1 -1
- package/dist/storage/{domains/operations → db}/index.d.ts +47 -2
- package/dist/storage/db/index.d.ts.map +1 -0
- package/dist/storage/db/utils.d.ts +25 -0
- package/dist/storage/db/utils.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +7 -9
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts +6 -7
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/scores/index.d.ts +6 -7
- package/dist/storage/domains/scores/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +9 -16
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +37 -35
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +5 -6
- package/dist/storage/domains/operations/index.d.ts.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
var error = require('@mastra/core/error');
|
|
4
4
|
var storage = require('@mastra/core/storage');
|
|
5
|
-
var
|
|
6
|
-
var
|
|
5
|
+
var sql = require('mssql');
|
|
6
|
+
var base = require('@mastra/core/base');
|
|
7
7
|
var utils = require('@mastra/core/utils');
|
|
8
|
+
var agent = require('@mastra/core/agent');
|
|
8
9
|
var crypto = require('crypto');
|
|
9
10
|
var evals = require('@mastra/core/evals');
|
|
10
11
|
|
|
11
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
13
|
|
|
13
|
-
var
|
|
14
|
+
var sql__default = /*#__PURE__*/_interopDefault(sql);
|
|
14
15
|
|
|
15
16
|
// src/storage/index.ts
|
|
16
17
|
function getSchemaName(schema) {
|
|
@@ -22,2273 +23,2321 @@ function getTableName({ indexName, schemaName }) {
|
|
|
22
23
|
const quotedSchemaName = schemaName;
|
|
23
24
|
return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
|
|
27
|
+
// src/storage/db/index.ts
|
|
28
|
+
function resolveMssqlConfig(config) {
|
|
29
|
+
if ("pool" in config && "db" in config) {
|
|
30
|
+
return { pool: config.pool, db: config.db, schema: config.schema, needsConnect: false };
|
|
31
|
+
}
|
|
32
|
+
const pool = new sql__default.default.ConnectionPool({
|
|
33
|
+
server: config.server,
|
|
34
|
+
database: config.database,
|
|
35
|
+
user: config.user,
|
|
36
|
+
password: config.password,
|
|
37
|
+
port: config.port,
|
|
38
|
+
options: config.options || { encrypt: true, trustServerCertificate: true }
|
|
39
|
+
});
|
|
40
|
+
const db = new MssqlDB({ pool, schemaName: config.schemaName });
|
|
41
|
+
return { pool, db, schema: config.schemaName, needsConnect: true };
|
|
37
42
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
49
|
-
} else if (key.endsWith("_lte")) {
|
|
50
|
-
const paramName = `p${paramIndex++}`;
|
|
51
|
-
const fieldName = key.slice(0, -4);
|
|
52
|
-
conditions.push(`[${utils.parseSqlIdentifier(fieldName, "field name")}] <= @${paramName}`);
|
|
53
|
-
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
54
|
-
} else if (value === null) {
|
|
55
|
-
conditions.push(`[${utils.parseSqlIdentifier(key, "field name")}] IS NULL`);
|
|
56
|
-
} else if (isInOperator(value)) {
|
|
57
|
-
const inValues = value.$in;
|
|
58
|
-
if (inValues.length === 0) {
|
|
59
|
-
conditions.push("1 = 0");
|
|
60
|
-
} else if (inValues.length === 1) {
|
|
61
|
-
const paramName = `p${paramIndex++}`;
|
|
62
|
-
conditions.push(`[${utils.parseSqlIdentifier(key, "field name")}] = @${paramName}`);
|
|
63
|
-
params[paramName] = inValues[0] instanceof Date ? inValues[0].toISOString() : inValues[0];
|
|
64
|
-
} else {
|
|
65
|
-
const inParamNames = [];
|
|
66
|
-
for (const item of inValues) {
|
|
67
|
-
const paramName = `p${paramIndex++}`;
|
|
68
|
-
inParamNames.push(`@${paramName}`);
|
|
69
|
-
params[paramName] = item instanceof Date ? item.toISOString() : item;
|
|
70
|
-
}
|
|
71
|
-
conditions.push(`[${utils.parseSqlIdentifier(key, "field name")}] IN (${inParamNames.join(", ")})`);
|
|
72
|
-
}
|
|
73
|
-
} else if (Array.isArray(value)) {
|
|
74
|
-
if (value.length === 0) {
|
|
75
|
-
conditions.push("1 = 0");
|
|
76
|
-
} else if (value.length === 1) {
|
|
77
|
-
const paramName = `p${paramIndex++}`;
|
|
78
|
-
conditions.push(`[${utils.parseSqlIdentifier(key, "field name")}] = @${paramName}`);
|
|
79
|
-
params[paramName] = value[0] instanceof Date ? value[0].toISOString() : value[0];
|
|
80
|
-
} else {
|
|
81
|
-
const inParamNames = [];
|
|
82
|
-
for (const item of value) {
|
|
83
|
-
const paramName = `p${paramIndex++}`;
|
|
84
|
-
inParamNames.push(`@${paramName}`);
|
|
85
|
-
params[paramName] = item instanceof Date ? item.toISOString() : item;
|
|
43
|
+
var MssqlDB = class extends base.MastraBase {
|
|
44
|
+
pool;
|
|
45
|
+
schemaName;
|
|
46
|
+
setupSchemaPromise = null;
|
|
47
|
+
schemaSetupComplete = void 0;
|
|
48
|
+
getSqlType(type, isPrimaryKey = false, useLargeStorage = false) {
|
|
49
|
+
switch (type) {
|
|
50
|
+
case "text":
|
|
51
|
+
if (useLargeStorage) {
|
|
52
|
+
return "NVARCHAR(MAX)";
|
|
86
53
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
54
|
+
return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(400)";
|
|
55
|
+
case "timestamp":
|
|
56
|
+
return "DATETIME2(7)";
|
|
57
|
+
case "uuid":
|
|
58
|
+
return "UNIQUEIDENTIFIER";
|
|
59
|
+
case "jsonb":
|
|
60
|
+
return "NVARCHAR(MAX)";
|
|
61
|
+
case "integer":
|
|
62
|
+
return "INT";
|
|
63
|
+
case "bigint":
|
|
64
|
+
return "BIGINT";
|
|
65
|
+
case "float":
|
|
66
|
+
return "FLOAT";
|
|
67
|
+
case "boolean":
|
|
68
|
+
return "BIT";
|
|
69
|
+
default:
|
|
70
|
+
throw new error.MastraError({
|
|
71
|
+
id: storage.createStorageErrorId("MSSQL", "TYPE", "NOT_SUPPORTED"),
|
|
72
|
+
domain: error.ErrorDomain.STORAGE,
|
|
73
|
+
category: error.ErrorCategory.THIRD_PARTY
|
|
74
|
+
});
|
|
93
75
|
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
result[key] = new Date(value);
|
|
116
|
-
} else if (columnSchema?.type === "timestamp" && value instanceof Date) {
|
|
117
|
-
result[key] = value;
|
|
118
|
-
} else if (columnSchema?.type === "boolean") {
|
|
119
|
-
result[key] = Boolean(value);
|
|
120
|
-
} else {
|
|
121
|
-
result[key] = value;
|
|
76
|
+
}
|
|
77
|
+
constructor({ pool, schemaName }) {
|
|
78
|
+
super({ component: "STORAGE", name: "MssqlDB" });
|
|
79
|
+
this.pool = pool;
|
|
80
|
+
this.schemaName = schemaName;
|
|
81
|
+
}
|
|
82
|
+
async hasColumn(table, column) {
|
|
83
|
+
const schema = this.schemaName || "dbo";
|
|
84
|
+
const request = this.pool.request();
|
|
85
|
+
request.input("schema", schema);
|
|
86
|
+
request.input("table", table);
|
|
87
|
+
request.input("column", column);
|
|
88
|
+
request.input("columnLower", column.toLowerCase());
|
|
89
|
+
const result = await request.query(
|
|
90
|
+
`SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND (COLUMN_NAME = @column OR COLUMN_NAME = @columnLower)`
|
|
91
|
+
);
|
|
92
|
+
return result.recordset.length > 0;
|
|
93
|
+
}
|
|
94
|
+
async setupSchema() {
|
|
95
|
+
if (!this.schemaName || this.schemaSetupComplete) {
|
|
96
|
+
return;
|
|
122
97
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// src/storage/domains/memory/index.ts
|
|
128
|
-
var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
129
|
-
pool;
|
|
130
|
-
schema;
|
|
131
|
-
operations;
|
|
132
|
-
_parseAndFormatMessages(messages, format) {
|
|
133
|
-
const messagesWithParsedContent = messages.map((message) => {
|
|
134
|
-
if (typeof message.content === "string") {
|
|
98
|
+
if (!this.setupSchemaPromise) {
|
|
99
|
+
this.setupSchemaPromise = (async () => {
|
|
135
100
|
try {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
101
|
+
const checkRequest = this.pool.request();
|
|
102
|
+
checkRequest.input("schemaName", this.schemaName);
|
|
103
|
+
const checkResult = await checkRequest.query(`
|
|
104
|
+
SELECT 1 AS found FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @schemaName
|
|
105
|
+
`);
|
|
106
|
+
const schemaExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
107
|
+
if (!schemaExists) {
|
|
108
|
+
try {
|
|
109
|
+
await this.pool.request().query(`CREATE SCHEMA [${this.schemaName}]`);
|
|
110
|
+
this.logger?.info?.(`Schema "${this.schemaName}" created successfully`);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
this.logger?.error?.(`Failed to create schema "${this.schemaName}"`, { error });
|
|
113
|
+
throw new Error(
|
|
114
|
+
`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.`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
this.schemaSetupComplete = true;
|
|
119
|
+
this.logger?.debug?.(`Schema "${this.schemaName}" is ready for use`);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
this.schemaSetupComplete = void 0;
|
|
122
|
+
this.setupSchemaPromise = null;
|
|
123
|
+
throw error;
|
|
124
|
+
} finally {
|
|
125
|
+
this.setupSchemaPromise = null;
|
|
139
126
|
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const cleanMessages = messagesWithParsedContent.map(({ seq_id, ...rest }) => rest);
|
|
144
|
-
const list = new agent.MessageList().add(cleanMessages, "memory");
|
|
145
|
-
return format === "v2" ? list.get.all.db() : list.get.all.v1();
|
|
127
|
+
})();
|
|
128
|
+
}
|
|
129
|
+
await this.setupSchemaPromise;
|
|
146
130
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
131
|
+
async insert({
|
|
132
|
+
tableName,
|
|
133
|
+
record,
|
|
134
|
+
transaction
|
|
151
135
|
}) {
|
|
152
|
-
super();
|
|
153
|
-
this.pool = pool;
|
|
154
|
-
this.schema = schema;
|
|
155
|
-
this.operations = operations;
|
|
156
|
-
}
|
|
157
|
-
async getThreadById({ threadId }) {
|
|
158
136
|
try {
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
[
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
...thread,
|
|
177
|
-
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
178
|
-
createdAt: thread.createdAt,
|
|
179
|
-
updatedAt: thread.updatedAt
|
|
180
|
-
};
|
|
137
|
+
const columns = Object.keys(record);
|
|
138
|
+
const parsedColumns = columns.map((col) => utils.parseSqlIdentifier(col, "column name"));
|
|
139
|
+
const paramNames = columns.map((_, i) => `@param${i}`);
|
|
140
|
+
const insertSql = `INSERT INTO ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (${parsedColumns.map((c) => `[${c}]`).join(", ")}) VALUES (${paramNames.join(", ")})`;
|
|
141
|
+
const request = transaction ? transaction.request() : this.pool.request();
|
|
142
|
+
columns.forEach((col, i) => {
|
|
143
|
+
const value = record[col];
|
|
144
|
+
const preparedValue = this.prepareValue(value, col, tableName);
|
|
145
|
+
if (preparedValue instanceof Date) {
|
|
146
|
+
request.input(`param${i}`, sql__default.default.DateTime2, preparedValue);
|
|
147
|
+
} else if (preparedValue === null || preparedValue === void 0) {
|
|
148
|
+
request.input(`param${i}`, this.getMssqlType(tableName, col), null);
|
|
149
|
+
} else {
|
|
150
|
+
request.input(`param${i}`, preparedValue);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
await request.query(insertSql);
|
|
181
154
|
} catch (error$1) {
|
|
182
155
|
throw new error.MastraError(
|
|
183
156
|
{
|
|
184
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
157
|
+
id: storage.createStorageErrorId("MSSQL", "INSERT", "FAILED"),
|
|
185
158
|
domain: error.ErrorDomain.STORAGE,
|
|
186
159
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
187
160
|
details: {
|
|
188
|
-
|
|
161
|
+
tableName
|
|
189
162
|
}
|
|
190
163
|
},
|
|
191
164
|
error$1
|
|
192
165
|
);
|
|
193
166
|
}
|
|
194
167
|
}
|
|
195
|
-
async
|
|
196
|
-
const
|
|
197
|
-
if (page < 0) {
|
|
198
|
-
throw new error.MastraError({
|
|
199
|
-
id: storage.createStorageErrorId("MSSQL", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
|
|
200
|
-
domain: error.ErrorDomain.STORAGE,
|
|
201
|
-
category: error.ErrorCategory.USER,
|
|
202
|
-
text: "Page number must be non-negative",
|
|
203
|
-
details: {
|
|
204
|
-
resourceId,
|
|
205
|
-
page
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
const perPage = storage.normalizePerPage(perPageInput, 100);
|
|
210
|
-
const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
|
|
211
|
-
const { field, direction } = this.parseOrderBy(orderBy);
|
|
168
|
+
async clearTable({ tableName }) {
|
|
169
|
+
const fullTableName = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
|
|
212
170
|
try {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
threads: [],
|
|
222
|
-
total: 0,
|
|
223
|
-
page,
|
|
224
|
-
perPage: perPageForResponse,
|
|
225
|
-
hasMore: false
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
const orderByField = field === "createdAt" ? "[createdAt]" : "[updatedAt]";
|
|
229
|
-
const dir = (direction || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
|
|
230
|
-
const limitValue = perPageInput === false ? total : perPage;
|
|
231
|
-
const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir} OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
232
|
-
const dataRequest = this.pool.request();
|
|
233
|
-
dataRequest.input("resourceId", resourceId);
|
|
234
|
-
dataRequest.input("offset", offset);
|
|
235
|
-
if (limitValue > 2147483647) {
|
|
236
|
-
dataRequest.input("perPage", sql3__default.default.BigInt, limitValue);
|
|
237
|
-
} else {
|
|
238
|
-
dataRequest.input("perPage", limitValue);
|
|
171
|
+
try {
|
|
172
|
+
await this.pool.request().query(`TRUNCATE TABLE ${fullTableName}`);
|
|
173
|
+
} catch (truncateError) {
|
|
174
|
+
if (truncateError?.number === 4712) {
|
|
175
|
+
await this.pool.request().query(`DELETE FROM ${fullTableName}`);
|
|
176
|
+
} else {
|
|
177
|
+
throw truncateError;
|
|
178
|
+
}
|
|
239
179
|
}
|
|
240
|
-
const rowsResult = await dataRequest.query(dataQuery);
|
|
241
|
-
const rows = rowsResult.recordset || [];
|
|
242
|
-
const threads = rows.map((thread) => ({
|
|
243
|
-
...thread,
|
|
244
|
-
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
245
|
-
createdAt: thread.createdAt,
|
|
246
|
-
updatedAt: thread.updatedAt
|
|
247
|
-
}));
|
|
248
|
-
return {
|
|
249
|
-
threads,
|
|
250
|
-
total,
|
|
251
|
-
page,
|
|
252
|
-
perPage: perPageForResponse,
|
|
253
|
-
hasMore: perPageInput === false ? false : offset + perPage < total
|
|
254
|
-
};
|
|
255
180
|
} catch (error$1) {
|
|
256
|
-
|
|
181
|
+
throw new error.MastraError(
|
|
257
182
|
{
|
|
258
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
183
|
+
id: storage.createStorageErrorId("MSSQL", "CLEAR_TABLE", "FAILED"),
|
|
259
184
|
domain: error.ErrorDomain.STORAGE,
|
|
260
185
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
261
186
|
details: {
|
|
262
|
-
|
|
263
|
-
page
|
|
187
|
+
tableName
|
|
264
188
|
}
|
|
265
189
|
},
|
|
266
190
|
error$1
|
|
267
191
|
);
|
|
268
|
-
this.logger?.error?.(mastraError.toString());
|
|
269
|
-
this.logger?.trackException?.(mastraError);
|
|
270
|
-
return {
|
|
271
|
-
threads: [],
|
|
272
|
-
total: 0,
|
|
273
|
-
page,
|
|
274
|
-
perPage: perPageForResponse,
|
|
275
|
-
hasMore: false
|
|
276
|
-
};
|
|
277
192
|
}
|
|
278
193
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
metadata = @metadata,
|
|
290
|
-
[updatedAt] = @updatedAt
|
|
291
|
-
WHEN NOT MATCHED THEN
|
|
292
|
-
INSERT (id, [resourceId], title, metadata, [createdAt], [updatedAt])
|
|
293
|
-
VALUES (@id, @resourceId, @title, @metadata, @createdAt, @updatedAt);`;
|
|
294
|
-
const req = this.pool.request();
|
|
295
|
-
req.input("id", thread.id);
|
|
296
|
-
req.input("resourceId", thread.resourceId);
|
|
297
|
-
req.input("title", thread.title);
|
|
298
|
-
const metadata = thread.metadata ? JSON.stringify(thread.metadata) : null;
|
|
299
|
-
if (metadata === null) {
|
|
300
|
-
req.input("metadata", sql3__default.default.NVarChar, null);
|
|
301
|
-
} else {
|
|
302
|
-
req.input("metadata", metadata);
|
|
303
|
-
}
|
|
304
|
-
req.input("createdAt", sql3__default.default.DateTime2, thread.createdAt);
|
|
305
|
-
req.input("updatedAt", sql3__default.default.DateTime2, thread.updatedAt);
|
|
306
|
-
await req.query(mergeSql);
|
|
307
|
-
return thread;
|
|
308
|
-
} catch (error$1) {
|
|
309
|
-
throw new error.MastraError(
|
|
310
|
-
{
|
|
311
|
-
id: storage.createStorageErrorId("MSSQL", "SAVE_THREAD", "FAILED"),
|
|
312
|
-
domain: error.ErrorDomain.STORAGE,
|
|
313
|
-
category: error.ErrorCategory.THIRD_PARTY,
|
|
314
|
-
details: {
|
|
315
|
-
threadId: thread.id
|
|
316
|
-
}
|
|
317
|
-
},
|
|
318
|
-
error$1
|
|
319
|
-
);
|
|
194
|
+
getDefaultValue(type) {
|
|
195
|
+
switch (type) {
|
|
196
|
+
case "timestamp":
|
|
197
|
+
return "DEFAULT SYSUTCDATETIME()";
|
|
198
|
+
case "jsonb":
|
|
199
|
+
return "DEFAULT N'{}'";
|
|
200
|
+
case "boolean":
|
|
201
|
+
return "DEFAULT 0";
|
|
202
|
+
default:
|
|
203
|
+
return storage.getDefaultValue(type);
|
|
320
204
|
}
|
|
321
205
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
async updateThread({
|
|
326
|
-
id,
|
|
327
|
-
title,
|
|
328
|
-
metadata
|
|
206
|
+
async createTable({
|
|
207
|
+
tableName,
|
|
208
|
+
schema
|
|
329
209
|
}) {
|
|
330
|
-
const existingThread = await this.getThreadById({ threadId: id });
|
|
331
|
-
if (!existingThread) {
|
|
332
|
-
throw new error.MastraError({
|
|
333
|
-
id: storage.createStorageErrorId("MSSQL", "UPDATE_THREAD", "NOT_FOUND"),
|
|
334
|
-
domain: error.ErrorDomain.STORAGE,
|
|
335
|
-
category: error.ErrorCategory.USER,
|
|
336
|
-
text: `Thread ${id} not found`,
|
|
337
|
-
details: {
|
|
338
|
-
threadId: id,
|
|
339
|
-
title
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
const mergedMetadata = {
|
|
344
|
-
...existingThread.metadata,
|
|
345
|
-
...metadata
|
|
346
|
-
};
|
|
347
210
|
try {
|
|
348
|
-
const
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
211
|
+
const uniqueConstraintColumns = tableName === storage.TABLE_WORKFLOW_SNAPSHOT ? ["workflow_name", "run_id"] : [];
|
|
212
|
+
const largeDataColumns = [
|
|
213
|
+
"workingMemory",
|
|
214
|
+
"snapshot",
|
|
215
|
+
"metadata",
|
|
216
|
+
"content",
|
|
217
|
+
// messages.content - can be very long conversation content
|
|
218
|
+
"input",
|
|
219
|
+
// evals.input - test input data
|
|
220
|
+
"output",
|
|
221
|
+
// evals.output - test output data
|
|
222
|
+
"instructions",
|
|
223
|
+
// evals.instructions - evaluation instructions
|
|
224
|
+
"other"
|
|
225
|
+
// traces.other - additional trace data
|
|
226
|
+
];
|
|
227
|
+
const columns = Object.entries(schema).map(([name, def]) => {
|
|
228
|
+
const parsedName = utils.parseSqlIdentifier(name, "column name");
|
|
229
|
+
const constraints = [];
|
|
230
|
+
if (def.primaryKey) constraints.push("PRIMARY KEY");
|
|
231
|
+
if (!def.nullable) constraints.push("NOT NULL");
|
|
232
|
+
const isIndexed = !!def.primaryKey || uniqueConstraintColumns.includes(name);
|
|
233
|
+
const useLargeStorage = largeDataColumns.includes(name);
|
|
234
|
+
return `[${parsedName}] ${this.getSqlType(def.type, isIndexed, useLargeStorage)} ${constraints.join(" ")}`.trim();
|
|
235
|
+
}).join(",\n");
|
|
236
|
+
if (this.schemaName) {
|
|
237
|
+
await this.setupSchema();
|
|
365
238
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
})
|
|
239
|
+
const checkTableRequest = this.pool.request();
|
|
240
|
+
checkTableRequest.input(
|
|
241
|
+
"tableName",
|
|
242
|
+
getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) }).replace(/[[\]]/g, "").split(".").pop()
|
|
243
|
+
);
|
|
244
|
+
const checkTableSql = `SELECT 1 AS found FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @tableName`;
|
|
245
|
+
checkTableRequest.input("schema", this.schemaName || "dbo");
|
|
246
|
+
const checkTableResult = await checkTableRequest.query(checkTableSql);
|
|
247
|
+
const tableExists = Array.isArray(checkTableResult.recordset) && checkTableResult.recordset.length > 0;
|
|
248
|
+
if (!tableExists) {
|
|
249
|
+
const createSql = `CREATE TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} (
|
|
250
|
+
${columns}
|
|
251
|
+
)`;
|
|
252
|
+
await this.pool.request().query(createSql);
|
|
253
|
+
}
|
|
254
|
+
const columnCheckSql = `
|
|
255
|
+
SELECT 1 AS found
|
|
256
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
257
|
+
WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @tableName AND COLUMN_NAME = 'seq_id'
|
|
258
|
+
`;
|
|
259
|
+
const checkColumnRequest = this.pool.request();
|
|
260
|
+
checkColumnRequest.input("schema", this.schemaName || "dbo");
|
|
261
|
+
checkColumnRequest.input(
|
|
262
|
+
"tableName",
|
|
263
|
+
getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) }).replace(/[[\]]/g, "").split(".").pop()
|
|
264
|
+
);
|
|
265
|
+
const columnResult = await checkColumnRequest.query(columnCheckSql);
|
|
266
|
+
const columnExists = Array.isArray(columnResult.recordset) && columnResult.recordset.length > 0;
|
|
267
|
+
if (!columnExists) {
|
|
268
|
+
const alterSql = `ALTER TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} ADD seq_id BIGINT IDENTITY(1,1)`;
|
|
269
|
+
await this.pool.request().query(alterSql);
|
|
270
|
+
}
|
|
271
|
+
if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
|
|
272
|
+
const constraintName = "mastra_workflow_snapshot_workflow_name_run_id_key";
|
|
273
|
+
const checkConstraintSql = `SELECT 1 AS found FROM sys.key_constraints WHERE name = @constraintName`;
|
|
274
|
+
const checkConstraintRequest = this.pool.request();
|
|
275
|
+
checkConstraintRequest.input("constraintName", constraintName);
|
|
276
|
+
const constraintResult = await checkConstraintRequest.query(checkConstraintSql);
|
|
277
|
+
const constraintExists = Array.isArray(constraintResult.recordset) && constraintResult.recordset.length > 0;
|
|
278
|
+
if (!constraintExists) {
|
|
279
|
+
const addConstraintSql = `ALTER TABLE ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} ADD CONSTRAINT ${constraintName} UNIQUE ([workflow_name], [run_id])`;
|
|
280
|
+
await this.pool.request().query(addConstraintSql);
|
|
281
|
+
}
|
|
377
282
|
}
|
|
378
|
-
return {
|
|
379
|
-
...thread,
|
|
380
|
-
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
381
|
-
createdAt: thread.createdAt,
|
|
382
|
-
updatedAt: thread.updatedAt
|
|
383
|
-
};
|
|
384
283
|
} catch (error$1) {
|
|
385
284
|
throw new error.MastraError(
|
|
386
285
|
{
|
|
387
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
286
|
+
id: storage.createStorageErrorId("MSSQL", "CREATE_TABLE", "FAILED"),
|
|
388
287
|
domain: error.ErrorDomain.STORAGE,
|
|
389
288
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
390
289
|
details: {
|
|
391
|
-
|
|
392
|
-
title
|
|
290
|
+
tableName
|
|
393
291
|
}
|
|
394
292
|
},
|
|
395
293
|
error$1
|
|
396
294
|
);
|
|
397
295
|
}
|
|
398
296
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Alters table schema to add columns if they don't exist
|
|
299
|
+
* @param tableName Name of the table
|
|
300
|
+
* @param schema Schema of the table
|
|
301
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
302
|
+
*/
|
|
303
|
+
async alterTable({
|
|
304
|
+
tableName,
|
|
305
|
+
schema,
|
|
306
|
+
ifNotExists
|
|
307
|
+
}) {
|
|
308
|
+
const fullTableName = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
|
|
405
309
|
try {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
310
|
+
for (const columnName of ifNotExists) {
|
|
311
|
+
if (schema[columnName]) {
|
|
312
|
+
const columnCheckRequest = this.pool.request();
|
|
313
|
+
columnCheckRequest.input("tableName", fullTableName.replace(/[[\]]/g, "").split(".").pop());
|
|
314
|
+
columnCheckRequest.input("columnName", columnName);
|
|
315
|
+
columnCheckRequest.input("schema", this.schemaName || "dbo");
|
|
316
|
+
const checkSql = `SELECT 1 AS found FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @tableName AND COLUMN_NAME = @columnName`;
|
|
317
|
+
const checkResult = await columnCheckRequest.query(checkSql);
|
|
318
|
+
const columnExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
319
|
+
if (!columnExists) {
|
|
320
|
+
const columnDef = schema[columnName];
|
|
321
|
+
const largeDataColumns = [
|
|
322
|
+
"workingMemory",
|
|
323
|
+
"snapshot",
|
|
324
|
+
"metadata",
|
|
325
|
+
"content",
|
|
326
|
+
"input",
|
|
327
|
+
"output",
|
|
328
|
+
"instructions",
|
|
329
|
+
"other"
|
|
330
|
+
];
|
|
331
|
+
const useLargeStorage = largeDataColumns.includes(columnName);
|
|
332
|
+
const isIndexed = !!columnDef.primaryKey;
|
|
333
|
+
const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage);
|
|
334
|
+
const nullable = columnDef.nullable === false ? "NOT NULL" : "";
|
|
335
|
+
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
336
|
+
const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
|
|
337
|
+
const alterSql = `ALTER TABLE ${fullTableName} ADD [${parsedColumnName}] ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
338
|
+
await this.pool.request().query(alterSql);
|
|
339
|
+
this.logger?.debug?.(`Ensured column ${parsedColumnName} exists in table ${fullTableName}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
412
343
|
} catch (error$1) {
|
|
413
|
-
await tx.rollback().catch(() => {
|
|
414
|
-
});
|
|
415
344
|
throw new error.MastraError(
|
|
416
345
|
{
|
|
417
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
346
|
+
id: storage.createStorageErrorId("MSSQL", "ALTER_TABLE", "FAILED"),
|
|
418
347
|
domain: error.ErrorDomain.STORAGE,
|
|
419
348
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
420
349
|
details: {
|
|
421
|
-
|
|
350
|
+
tableName
|
|
422
351
|
}
|
|
423
352
|
},
|
|
424
353
|
error$1
|
|
425
354
|
);
|
|
426
355
|
}
|
|
427
356
|
}
|
|
428
|
-
async
|
|
429
|
-
if (!include || include.length === 0) return null;
|
|
430
|
-
const unionQueries = [];
|
|
431
|
-
const paramValues = [];
|
|
432
|
-
let paramIdx = 1;
|
|
433
|
-
const paramNames = [];
|
|
434
|
-
const tableName = getTableName({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
|
|
435
|
-
for (const inc of include) {
|
|
436
|
-
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
437
|
-
const pId = `@p${paramIdx}`;
|
|
438
|
-
const pPrev = `@p${paramIdx + 1}`;
|
|
439
|
-
const pNext = `@p${paramIdx + 2}`;
|
|
440
|
-
unionQueries.push(
|
|
441
|
-
`
|
|
442
|
-
SELECT
|
|
443
|
-
m.id,
|
|
444
|
-
m.content,
|
|
445
|
-
m.role,
|
|
446
|
-
m.type,
|
|
447
|
-
m.[createdAt],
|
|
448
|
-
m.thread_id AS threadId,
|
|
449
|
-
m.[resourceId],
|
|
450
|
-
m.seq_id
|
|
451
|
-
FROM (
|
|
452
|
-
SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
|
|
453
|
-
FROM ${tableName}
|
|
454
|
-
WHERE [thread_id] = (SELECT thread_id FROM ${tableName} WHERE id = ${pId})
|
|
455
|
-
) AS m
|
|
456
|
-
WHERE m.id = ${pId}
|
|
457
|
-
OR EXISTS (
|
|
458
|
-
SELECT 1
|
|
459
|
-
FROM (
|
|
460
|
-
SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
|
|
461
|
-
FROM ${tableName}
|
|
462
|
-
WHERE [thread_id] = (SELECT thread_id FROM ${tableName} WHERE id = ${pId})
|
|
463
|
-
) AS target
|
|
464
|
-
WHERE target.id = ${pId}
|
|
465
|
-
AND (
|
|
466
|
-
-- Get previous messages (messages that come BEFORE the target)
|
|
467
|
-
(m.row_num < target.row_num AND m.row_num >= target.row_num - ${pPrev})
|
|
468
|
-
OR
|
|
469
|
-
-- Get next messages (messages that come AFTER the target)
|
|
470
|
-
(m.row_num > target.row_num AND m.row_num <= target.row_num + ${pNext})
|
|
471
|
-
)
|
|
472
|
-
)
|
|
473
|
-
`
|
|
474
|
-
);
|
|
475
|
-
paramValues.push(id, withPreviousMessages, withNextMessages);
|
|
476
|
-
paramNames.push(`p${paramIdx}`, `p${paramIdx + 1}`, `p${paramIdx + 2}`);
|
|
477
|
-
paramIdx += 3;
|
|
478
|
-
}
|
|
479
|
-
const finalQuery = `
|
|
480
|
-
SELECT * FROM (
|
|
481
|
-
${unionQueries.join(" UNION ALL ")}
|
|
482
|
-
) AS union_result
|
|
483
|
-
ORDER BY [seq_id] ASC
|
|
484
|
-
`;
|
|
485
|
-
const req = this.pool.request();
|
|
486
|
-
for (let i = 0; i < paramValues.length; ++i) {
|
|
487
|
-
req.input(paramNames[i], paramValues[i]);
|
|
488
|
-
}
|
|
489
|
-
const result = await req.query(finalQuery);
|
|
490
|
-
const includedRows = result.recordset || [];
|
|
491
|
-
const seen = /* @__PURE__ */ new Set();
|
|
492
|
-
const dedupedRows = includedRows.filter((row) => {
|
|
493
|
-
if (seen.has(row.id)) return false;
|
|
494
|
-
seen.add(row.id);
|
|
495
|
-
return true;
|
|
496
|
-
});
|
|
497
|
-
return dedupedRows;
|
|
498
|
-
}
|
|
499
|
-
async listMessagesById({ messageIds }) {
|
|
500
|
-
if (messageIds.length === 0) return { messages: [] };
|
|
501
|
-
const selectStatement = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId`;
|
|
502
|
-
const orderByStatement = `ORDER BY [seq_id] DESC`;
|
|
357
|
+
async load({ tableName, keys }) {
|
|
503
358
|
try {
|
|
504
|
-
|
|
505
|
-
|
|
359
|
+
const keyEntries = Object.entries(keys).map(([key, value]) => [utils.parseSqlIdentifier(key, "column name"), value]);
|
|
360
|
+
const conditions = keyEntries.map(([key], i) => `[${key}] = @param${i}`).join(" AND ");
|
|
361
|
+
const sqlQuery = `SELECT * FROM ${getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) })} WHERE ${conditions}`;
|
|
506
362
|
const request = this.pool.request();
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
const timeDiff = a.seq_id - b.seq_id;
|
|
514
|
-
return timeDiff;
|
|
515
|
-
});
|
|
516
|
-
const messagesWithParsedContent = rows.map((row) => {
|
|
517
|
-
if (typeof row.content === "string") {
|
|
518
|
-
try {
|
|
519
|
-
return { ...row, content: JSON.parse(row.content) };
|
|
520
|
-
} catch {
|
|
521
|
-
return row;
|
|
522
|
-
}
|
|
363
|
+
keyEntries.forEach(([key, value], i) => {
|
|
364
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
365
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
366
|
+
request.input(`param${i}`, this.getMssqlType(tableName, key), null);
|
|
367
|
+
} else {
|
|
368
|
+
request.input(`param${i}`, preparedValue);
|
|
523
369
|
}
|
|
524
|
-
return row;
|
|
525
370
|
});
|
|
526
|
-
const
|
|
527
|
-
const
|
|
528
|
-
|
|
371
|
+
const resultSet = await request.query(sqlQuery);
|
|
372
|
+
const result = resultSet.recordset[0] || null;
|
|
373
|
+
if (!result) {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
|
|
377
|
+
const snapshot = result;
|
|
378
|
+
if (typeof snapshot.snapshot === "string") {
|
|
379
|
+
snapshot.snapshot = JSON.parse(snapshot.snapshot);
|
|
380
|
+
}
|
|
381
|
+
return snapshot;
|
|
382
|
+
}
|
|
383
|
+
return result;
|
|
529
384
|
} catch (error$1) {
|
|
530
|
-
|
|
385
|
+
throw new error.MastraError(
|
|
531
386
|
{
|
|
532
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
387
|
+
id: storage.createStorageErrorId("MSSQL", "LOAD", "FAILED"),
|
|
533
388
|
domain: error.ErrorDomain.STORAGE,
|
|
534
389
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
535
390
|
details: {
|
|
536
|
-
|
|
391
|
+
tableName
|
|
537
392
|
}
|
|
538
393
|
},
|
|
539
394
|
error$1
|
|
540
395
|
);
|
|
541
|
-
this.logger?.error?.(mastraError.toString());
|
|
542
|
-
this.logger?.trackException?.(mastraError);
|
|
543
|
-
return { messages: [] };
|
|
544
396
|
}
|
|
545
397
|
}
|
|
546
|
-
async
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
398
|
+
async batchInsert({ tableName, records }) {
|
|
399
|
+
const transaction = this.pool.transaction();
|
|
400
|
+
try {
|
|
401
|
+
await transaction.begin();
|
|
402
|
+
for (const record of records) {
|
|
403
|
+
await this.insert({ tableName, record, transaction });
|
|
404
|
+
}
|
|
405
|
+
await transaction.commit();
|
|
406
|
+
} catch (error$1) {
|
|
407
|
+
await transaction.rollback();
|
|
550
408
|
throw new error.MastraError(
|
|
551
409
|
{
|
|
552
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
410
|
+
id: storage.createStorageErrorId("MSSQL", "BATCH_INSERT", "FAILED"),
|
|
553
411
|
domain: error.ErrorDomain.STORAGE,
|
|
554
412
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
555
|
-
details: {
|
|
413
|
+
details: {
|
|
414
|
+
tableName,
|
|
415
|
+
numberOfRecords: records.length
|
|
416
|
+
}
|
|
556
417
|
},
|
|
557
|
-
|
|
418
|
+
error$1
|
|
558
419
|
);
|
|
559
420
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
id: storage.createStorageErrorId("MSSQL", "LIST_MESSAGES", "INVALID_PAGE"),
|
|
563
|
-
domain: error.ErrorDomain.STORAGE,
|
|
564
|
-
category: error.ErrorCategory.USER,
|
|
565
|
-
text: "Page number must be non-negative",
|
|
566
|
-
details: {
|
|
567
|
-
threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
|
|
568
|
-
page
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
const perPage = storage.normalizePerPage(perPageInput, 40);
|
|
573
|
-
const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
|
|
421
|
+
}
|
|
422
|
+
async dropTable({ tableName }) {
|
|
574
423
|
try {
|
|
575
|
-
const {
|
|
576
|
-
|
|
577
|
-
const tableName = getTableName({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
|
|
578
|
-
const baseQuery = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId FROM ${tableName}`;
|
|
579
|
-
const filters = {
|
|
580
|
-
thread_id: threadIds.length === 1 ? threadIds[0] : { $in: threadIds },
|
|
581
|
-
...resourceId ? { resourceId } : {},
|
|
582
|
-
...buildDateRangeFilter(filter?.dateRange, "createdAt")
|
|
583
|
-
};
|
|
584
|
-
const { sql: actualWhereClause = "", params: whereParams } = prepareWhereClause(
|
|
585
|
-
filters);
|
|
586
|
-
const bindWhereParams = (req) => {
|
|
587
|
-
Object.entries(whereParams).forEach(([paramName, paramValue]) => req.input(paramName, paramValue));
|
|
588
|
-
};
|
|
589
|
-
const countRequest = this.pool.request();
|
|
590
|
-
bindWhereParams(countRequest);
|
|
591
|
-
const countResult = await countRequest.query(`SELECT COUNT(*) as total FROM ${tableName}${actualWhereClause}`);
|
|
592
|
-
const total = parseInt(countResult.recordset[0]?.total, 10) || 0;
|
|
593
|
-
const fetchBaseMessages = async () => {
|
|
594
|
-
const request = this.pool.request();
|
|
595
|
-
bindWhereParams(request);
|
|
596
|
-
if (perPageInput === false) {
|
|
597
|
-
const result2 = await request.query(`${baseQuery}${actualWhereClause} ${orderByStatement}`);
|
|
598
|
-
return result2.recordset || [];
|
|
599
|
-
}
|
|
600
|
-
request.input("offset", offset);
|
|
601
|
-
request.input("limit", perPage > 2147483647 ? sql3__default.default.BigInt : sql3__default.default.Int, perPage);
|
|
602
|
-
const result = await request.query(
|
|
603
|
-
`${baseQuery}${actualWhereClause} ${orderByStatement} OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
|
|
604
|
-
);
|
|
605
|
-
return result.recordset || [];
|
|
606
|
-
};
|
|
607
|
-
const baseRows = perPage === 0 ? [] : await fetchBaseMessages();
|
|
608
|
-
const messages = [...baseRows];
|
|
609
|
-
const seqById = /* @__PURE__ */ new Map();
|
|
610
|
-
messages.forEach((msg) => {
|
|
611
|
-
if (typeof msg.seq_id === "number") seqById.set(msg.id, msg.seq_id);
|
|
612
|
-
});
|
|
613
|
-
if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
|
|
614
|
-
return {
|
|
615
|
-
messages: [],
|
|
616
|
-
total: 0,
|
|
617
|
-
page,
|
|
618
|
-
perPage: perPageForResponse,
|
|
619
|
-
hasMore: false
|
|
620
|
-
};
|
|
621
|
-
}
|
|
622
|
-
if (include?.length) {
|
|
623
|
-
const messageIds = new Set(messages.map((m) => m.id));
|
|
624
|
-
const includeMessages = await this._getIncludedMessages({ include });
|
|
625
|
-
includeMessages?.forEach((msg) => {
|
|
626
|
-
if (!messageIds.has(msg.id)) {
|
|
627
|
-
messages.push(msg);
|
|
628
|
-
messageIds.add(msg.id);
|
|
629
|
-
if (typeof msg.seq_id === "number") seqById.set(msg.id, msg.seq_id);
|
|
630
|
-
}
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
const parsed = this._parseAndFormatMessages(messages, "v2");
|
|
634
|
-
const mult = direction === "ASC" ? 1 : -1;
|
|
635
|
-
const finalMessages = parsed.sort((a, b) => {
|
|
636
|
-
const aVal = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
|
|
637
|
-
const bVal = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
|
|
638
|
-
if (aVal == null || bVal == null) {
|
|
639
|
-
return aVal == null && bVal == null ? a.id.localeCompare(b.id) : aVal == null ? 1 : -1;
|
|
640
|
-
}
|
|
641
|
-
const diff = (typeof aVal === "number" && typeof bVal === "number" ? aVal - bVal : String(aVal).localeCompare(String(bVal))) * mult;
|
|
642
|
-
if (diff !== 0) return diff;
|
|
643
|
-
const seqA = seqById.get(a.id);
|
|
644
|
-
const seqB = seqById.get(b.id);
|
|
645
|
-
return seqA != null && seqB != null ? (seqA - seqB) * mult : a.id.localeCompare(b.id);
|
|
646
|
-
});
|
|
647
|
-
const threadIdSet = new Set(threadIds);
|
|
648
|
-
const returnedThreadMessageCount = finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).length;
|
|
649
|
-
const hasMore = perPageInput !== false && returnedThreadMessageCount < total && offset + perPage < total;
|
|
650
|
-
return {
|
|
651
|
-
messages: finalMessages,
|
|
652
|
-
total,
|
|
653
|
-
page,
|
|
654
|
-
perPage: perPageForResponse,
|
|
655
|
-
hasMore
|
|
656
|
-
};
|
|
424
|
+
const tableNameWithSchema = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
|
|
425
|
+
await this.pool.request().query(`DROP TABLE IF EXISTS ${tableNameWithSchema}`);
|
|
657
426
|
} catch (error$1) {
|
|
658
|
-
|
|
427
|
+
throw new error.MastraError(
|
|
659
428
|
{
|
|
660
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
429
|
+
id: storage.createStorageErrorId("MSSQL", "DROP_TABLE", "FAILED"),
|
|
661
430
|
domain: error.ErrorDomain.STORAGE,
|
|
662
431
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
663
432
|
details: {
|
|
664
|
-
|
|
665
|
-
resourceId: resourceId ?? ""
|
|
433
|
+
tableName
|
|
666
434
|
}
|
|
667
435
|
},
|
|
668
436
|
error$1
|
|
669
437
|
);
|
|
670
|
-
this.logger?.error?.(mastraError.toString());
|
|
671
|
-
this.logger?.trackException?.(mastraError);
|
|
672
|
-
return {
|
|
673
|
-
messages: [],
|
|
674
|
-
total: 0,
|
|
675
|
-
page,
|
|
676
|
-
perPage: perPageForResponse,
|
|
677
|
-
hasMore: false
|
|
678
|
-
};
|
|
679
438
|
}
|
|
680
439
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
domain: error.ErrorDomain.STORAGE,
|
|
688
|
-
category: error.ErrorCategory.THIRD_PARTY,
|
|
689
|
-
text: `Thread ID is required`
|
|
690
|
-
});
|
|
440
|
+
/**
|
|
441
|
+
* Prepares a value for database operations, handling Date objects and JSON serialization
|
|
442
|
+
*/
|
|
443
|
+
prepareValue(value, columnName, tableName) {
|
|
444
|
+
if (value === null || value === void 0) {
|
|
445
|
+
return value;
|
|
691
446
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
throw new error.MastraError({
|
|
695
|
-
id: storage.createStorageErrorId("MSSQL", "SAVE_MESSAGES", "THREAD_NOT_FOUND"),
|
|
696
|
-
domain: error.ErrorDomain.STORAGE,
|
|
697
|
-
category: error.ErrorCategory.THIRD_PARTY,
|
|
698
|
-
text: `Thread ${threadId} not found`,
|
|
699
|
-
details: { threadId }
|
|
700
|
-
});
|
|
447
|
+
if (value instanceof Date) {
|
|
448
|
+
return value;
|
|
701
449
|
}
|
|
702
|
-
const
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
|
|
712
|
-
);
|
|
713
|
-
}
|
|
714
|
-
if (!message.resourceId) {
|
|
715
|
-
throw new Error(
|
|
716
|
-
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
|
|
717
|
-
);
|
|
718
|
-
}
|
|
719
|
-
const request = transaction.request();
|
|
720
|
-
request.input("id", message.id);
|
|
721
|
-
request.input("thread_id", message.threadId);
|
|
722
|
-
request.input(
|
|
723
|
-
"content",
|
|
724
|
-
typeof message.content === "string" ? message.content : JSON.stringify(message.content)
|
|
725
|
-
);
|
|
726
|
-
request.input("createdAt", sql3__default.default.DateTime2, message.createdAt);
|
|
727
|
-
request.input("role", message.role);
|
|
728
|
-
request.input("type", message.type || "v2");
|
|
729
|
-
request.input("resourceId", message.resourceId);
|
|
730
|
-
const mergeSql = `MERGE INTO ${tableMessages} AS target
|
|
731
|
-
USING (SELECT @id AS id) AS src
|
|
732
|
-
ON target.id = src.id
|
|
733
|
-
WHEN MATCHED THEN UPDATE SET
|
|
734
|
-
thread_id = @thread_id,
|
|
735
|
-
content = @content,
|
|
736
|
-
[createdAt] = @createdAt,
|
|
737
|
-
role = @role,
|
|
738
|
-
type = @type,
|
|
739
|
-
resourceId = @resourceId
|
|
740
|
-
WHEN NOT MATCHED THEN INSERT (id, thread_id, content, [createdAt], role, type, resourceId)
|
|
741
|
-
VALUES (@id, @thread_id, @content, @createdAt, @role, @type, @resourceId);`;
|
|
742
|
-
await request.query(mergeSql);
|
|
743
|
-
}
|
|
744
|
-
const threadReq = transaction.request();
|
|
745
|
-
threadReq.input("updatedAt", sql3__default.default.DateTime2, /* @__PURE__ */ new Date());
|
|
746
|
-
threadReq.input("id", threadId);
|
|
747
|
-
await threadReq.query(`UPDATE ${tableThreads} SET [updatedAt] = @updatedAt WHERE id = @id`);
|
|
748
|
-
await transaction.commit();
|
|
749
|
-
} catch (error) {
|
|
750
|
-
await transaction.rollback();
|
|
751
|
-
throw error;
|
|
752
|
-
}
|
|
753
|
-
const messagesWithParsedContent = messages.map((message) => {
|
|
754
|
-
if (typeof message.content === "string") {
|
|
450
|
+
const schema = storage.TABLE_SCHEMAS[tableName];
|
|
451
|
+
const columnSchema = schema?.[columnName];
|
|
452
|
+
if (columnSchema?.type === "boolean") {
|
|
453
|
+
return value ? 1 : 0;
|
|
454
|
+
}
|
|
455
|
+
if (columnSchema?.type === "jsonb") {
|
|
456
|
+
if (typeof value === "string") {
|
|
457
|
+
const trimmed = value.trim();
|
|
458
|
+
if (trimmed.length > 0) {
|
|
755
459
|
try {
|
|
756
|
-
|
|
460
|
+
JSON.parse(trimmed);
|
|
461
|
+
return trimmed;
|
|
757
462
|
} catch {
|
|
758
|
-
return message;
|
|
759
463
|
}
|
|
760
464
|
}
|
|
761
|
-
return
|
|
465
|
+
return JSON.stringify(value);
|
|
466
|
+
}
|
|
467
|
+
if (typeof value === "bigint") {
|
|
468
|
+
return value.toString();
|
|
469
|
+
}
|
|
470
|
+
return JSON.stringify(value);
|
|
471
|
+
}
|
|
472
|
+
if (typeof value === "object") {
|
|
473
|
+
return JSON.stringify(value);
|
|
474
|
+
}
|
|
475
|
+
return value;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Maps TABLE_SCHEMAS types to mssql param types (used when value is null)
|
|
479
|
+
*/
|
|
480
|
+
getMssqlType(tableName, columnName) {
|
|
481
|
+
const col = storage.TABLE_SCHEMAS[tableName]?.[columnName];
|
|
482
|
+
switch (col?.type) {
|
|
483
|
+
case "text":
|
|
484
|
+
return sql__default.default.NVarChar;
|
|
485
|
+
case "timestamp":
|
|
486
|
+
return sql__default.default.DateTime2;
|
|
487
|
+
case "uuid":
|
|
488
|
+
return sql__default.default.UniqueIdentifier;
|
|
489
|
+
case "jsonb":
|
|
490
|
+
return sql__default.default.NVarChar;
|
|
491
|
+
case "integer":
|
|
492
|
+
return sql__default.default.Int;
|
|
493
|
+
case "bigint":
|
|
494
|
+
return sql__default.default.BigInt;
|
|
495
|
+
case "float":
|
|
496
|
+
return sql__default.default.Float;
|
|
497
|
+
case "boolean":
|
|
498
|
+
return sql__default.default.Bit;
|
|
499
|
+
default:
|
|
500
|
+
return sql__default.default.NVarChar;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Update a single record in the database
|
|
505
|
+
*/
|
|
506
|
+
async update({
|
|
507
|
+
tableName,
|
|
508
|
+
keys,
|
|
509
|
+
data,
|
|
510
|
+
transaction
|
|
511
|
+
}) {
|
|
512
|
+
try {
|
|
513
|
+
if (!data || Object.keys(data).length === 0) {
|
|
514
|
+
throw new error.MastraError({
|
|
515
|
+
id: storage.createStorageErrorId("MSSQL", "UPDATE", "EMPTY_DATA"),
|
|
516
|
+
domain: error.ErrorDomain.STORAGE,
|
|
517
|
+
category: error.ErrorCategory.USER,
|
|
518
|
+
text: "Cannot update with empty data payload"
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
if (!keys || Object.keys(keys).length === 0) {
|
|
522
|
+
throw new error.MastraError({
|
|
523
|
+
id: storage.createStorageErrorId("MSSQL", "UPDATE", "EMPTY_KEYS"),
|
|
524
|
+
domain: error.ErrorDomain.STORAGE,
|
|
525
|
+
category: error.ErrorCategory.USER,
|
|
526
|
+
text: "Cannot update without keys to identify records"
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
const setClauses = [];
|
|
530
|
+
const request = transaction ? transaction.request() : this.pool.request();
|
|
531
|
+
let paramIndex = 0;
|
|
532
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
533
|
+
const parsedKey = utils.parseSqlIdentifier(key, "column name");
|
|
534
|
+
const paramName = `set${paramIndex++}`;
|
|
535
|
+
setClauses.push(`[${parsedKey}] = @${paramName}`);
|
|
536
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
537
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
538
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
539
|
+
} else {
|
|
540
|
+
request.input(paramName, preparedValue);
|
|
541
|
+
}
|
|
762
542
|
});
|
|
763
|
-
const
|
|
764
|
-
|
|
543
|
+
const whereConditions = [];
|
|
544
|
+
Object.entries(keys).forEach(([key, value]) => {
|
|
545
|
+
const parsedKey = utils.parseSqlIdentifier(key, "column name");
|
|
546
|
+
const paramName = `where${paramIndex++}`;
|
|
547
|
+
whereConditions.push(`[${parsedKey}] = @${paramName}`);
|
|
548
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
549
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
550
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
551
|
+
} else {
|
|
552
|
+
request.input(paramName, preparedValue);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
const tableName_ = getTableName({
|
|
556
|
+
indexName: tableName,
|
|
557
|
+
schemaName: getSchemaName(this.schemaName)
|
|
558
|
+
});
|
|
559
|
+
const updateSql = `UPDATE ${tableName_} SET ${setClauses.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
|
|
560
|
+
await request.query(updateSql);
|
|
765
561
|
} catch (error$1) {
|
|
766
562
|
throw new error.MastraError(
|
|
767
563
|
{
|
|
768
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
564
|
+
id: storage.createStorageErrorId("MSSQL", "UPDATE", "FAILED"),
|
|
769
565
|
domain: error.ErrorDomain.STORAGE,
|
|
770
566
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
771
|
-
details: {
|
|
567
|
+
details: {
|
|
568
|
+
tableName
|
|
569
|
+
}
|
|
772
570
|
},
|
|
773
571
|
error$1
|
|
774
572
|
);
|
|
775
573
|
}
|
|
776
574
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
const idParams = messageIds.map((_, i) => `@id${i}`).join(", ");
|
|
785
|
-
let selectQuery = `SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId FROM ${getTableName({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })}`;
|
|
786
|
-
if (idParams.length > 0) {
|
|
787
|
-
selectQuery += ` WHERE id IN (${idParams})`;
|
|
788
|
-
} else {
|
|
789
|
-
return [];
|
|
790
|
-
}
|
|
791
|
-
const selectReq = this.pool.request();
|
|
792
|
-
messageIds.forEach((id, i) => selectReq.input(`id${i}`, id));
|
|
793
|
-
const existingMessagesDb = (await selectReq.query(selectQuery)).recordset;
|
|
794
|
-
if (!existingMessagesDb || existingMessagesDb.length === 0) {
|
|
795
|
-
return [];
|
|
796
|
-
}
|
|
797
|
-
const existingMessages = existingMessagesDb.map((msg) => {
|
|
798
|
-
if (typeof msg.content === "string") {
|
|
799
|
-
try {
|
|
800
|
-
msg.content = JSON.parse(msg.content);
|
|
801
|
-
} catch {
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
return msg;
|
|
805
|
-
});
|
|
806
|
-
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
575
|
+
/**
|
|
576
|
+
* Update multiple records in a single batch transaction
|
|
577
|
+
*/
|
|
578
|
+
async batchUpdate({
|
|
579
|
+
tableName,
|
|
580
|
+
updates
|
|
581
|
+
}) {
|
|
807
582
|
const transaction = this.pool.transaction();
|
|
808
583
|
try {
|
|
809
584
|
await transaction.begin();
|
|
810
|
-
for (const
|
|
811
|
-
|
|
812
|
-
if (!updatePayload) continue;
|
|
813
|
-
const { id, ...fieldsToUpdate } = updatePayload;
|
|
814
|
-
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
815
|
-
threadIdsToUpdate.add(existingMessage.threadId);
|
|
816
|
-
if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
|
|
817
|
-
threadIdsToUpdate.add(updatePayload.threadId);
|
|
818
|
-
}
|
|
819
|
-
const setClauses = [];
|
|
820
|
-
const req = transaction.request();
|
|
821
|
-
req.input("id", id);
|
|
822
|
-
const columnMapping = { threadId: "thread_id" };
|
|
823
|
-
const updatableFields = { ...fieldsToUpdate };
|
|
824
|
-
if (updatableFields.content) {
|
|
825
|
-
const newContent = {
|
|
826
|
-
...existingMessage.content,
|
|
827
|
-
...updatableFields.content,
|
|
828
|
-
...existingMessage.content?.metadata && updatableFields.content.metadata ? { metadata: { ...existingMessage.content.metadata, ...updatableFields.content.metadata } } : {}
|
|
829
|
-
};
|
|
830
|
-
setClauses.push(`content = @content`);
|
|
831
|
-
req.input("content", JSON.stringify(newContent));
|
|
832
|
-
delete updatableFields.content;
|
|
833
|
-
}
|
|
834
|
-
for (const key in updatableFields) {
|
|
835
|
-
if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
|
|
836
|
-
const dbColumn = columnMapping[key] || key;
|
|
837
|
-
setClauses.push(`[${dbColumn}] = @${dbColumn}`);
|
|
838
|
-
req.input(dbColumn, updatableFields[key]);
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
if (setClauses.length > 0) {
|
|
842
|
-
const updateSql = `UPDATE ${getTableName({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName(this.schema) })} SET ${setClauses.join(", ")} WHERE id = @id`;
|
|
843
|
-
await req.query(updateSql);
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
if (threadIdsToUpdate.size > 0) {
|
|
847
|
-
const threadIdParams = Array.from(threadIdsToUpdate).map((_, i) => `@tid${i}`).join(", ");
|
|
848
|
-
const threadReq = transaction.request();
|
|
849
|
-
Array.from(threadIdsToUpdate).forEach((tid, i) => threadReq.input(`tid${i}`, tid));
|
|
850
|
-
threadReq.input("updatedAt", (/* @__PURE__ */ new Date()).toISOString());
|
|
851
|
-
const threadSql = `UPDATE ${getTableName({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName(this.schema) })} SET updatedAt = @updatedAt WHERE id IN (${threadIdParams})`;
|
|
852
|
-
await threadReq.query(threadSql);
|
|
585
|
+
for (const { keys, data } of updates) {
|
|
586
|
+
await this.update({ tableName, keys, data, transaction });
|
|
853
587
|
}
|
|
854
588
|
await transaction.commit();
|
|
855
589
|
} catch (error$1) {
|
|
856
590
|
await transaction.rollback();
|
|
857
591
|
throw new error.MastraError(
|
|
858
592
|
{
|
|
859
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
593
|
+
id: storage.createStorageErrorId("MSSQL", "BATCH_UPDATE", "FAILED"),
|
|
860
594
|
domain: error.ErrorDomain.STORAGE,
|
|
861
|
-
category: error.ErrorCategory.THIRD_PARTY
|
|
595
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
596
|
+
details: {
|
|
597
|
+
tableName,
|
|
598
|
+
numberOfRecords: updates.length
|
|
599
|
+
}
|
|
862
600
|
},
|
|
863
601
|
error$1
|
|
864
602
|
);
|
|
865
603
|
}
|
|
866
|
-
const refetchReq = this.pool.request();
|
|
867
|
-
messageIds.forEach((id, i) => refetchReq.input(`id${i}`, id));
|
|
868
|
-
const updatedMessages = (await refetchReq.query(selectQuery)).recordset;
|
|
869
|
-
return (updatedMessages || []).map((message) => {
|
|
870
|
-
if (typeof message.content === "string") {
|
|
871
|
-
try {
|
|
872
|
-
message.content = JSON.parse(message.content);
|
|
873
|
-
} catch {
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
return message;
|
|
877
|
-
});
|
|
878
604
|
}
|
|
879
|
-
|
|
880
|
-
|
|
605
|
+
/**
|
|
606
|
+
* Delete multiple records by keys
|
|
607
|
+
*/
|
|
608
|
+
async batchDelete({ tableName, keys }) {
|
|
609
|
+
if (keys.length === 0) {
|
|
881
610
|
return;
|
|
882
611
|
}
|
|
612
|
+
const tableName_ = getTableName({
|
|
613
|
+
indexName: tableName,
|
|
614
|
+
schemaName: getSchemaName(this.schemaName)
|
|
615
|
+
});
|
|
616
|
+
const transaction = this.pool.transaction();
|
|
883
617
|
try {
|
|
884
|
-
const messageTableName = getTableName({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName(this.schema) });
|
|
885
|
-
const threadTableName = getTableName({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName(this.schema) });
|
|
886
|
-
const placeholders = messageIds.map((_, idx) => `@p${idx + 1}`).join(",");
|
|
887
|
-
const request = this.pool.request();
|
|
888
|
-
messageIds.forEach((id, idx) => {
|
|
889
|
-
request.input(`p${idx + 1}`, id);
|
|
890
|
-
});
|
|
891
|
-
const messages = await request.query(
|
|
892
|
-
`SELECT DISTINCT [thread_id] FROM ${messageTableName} WHERE [id] IN (${placeholders})`
|
|
893
|
-
);
|
|
894
|
-
const threadIds = messages.recordset?.map((msg) => msg.thread_id).filter(Boolean) || [];
|
|
895
|
-
const transaction = this.pool.transaction();
|
|
896
618
|
await transaction.begin();
|
|
897
|
-
|
|
898
|
-
const
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
619
|
+
for (const keySet of keys) {
|
|
620
|
+
const conditions = [];
|
|
621
|
+
const request = transaction.request();
|
|
622
|
+
let paramIndex = 0;
|
|
623
|
+
Object.entries(keySet).forEach(([key, value]) => {
|
|
624
|
+
const parsedKey = utils.parseSqlIdentifier(key, "column name");
|
|
625
|
+
const paramName = `p${paramIndex++}`;
|
|
626
|
+
conditions.push(`[${parsedKey}] = @${paramName}`);
|
|
627
|
+
const preparedValue = this.prepareValue(value, key, tableName);
|
|
628
|
+
if (preparedValue === null || preparedValue === void 0) {
|
|
629
|
+
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
630
|
+
} else {
|
|
631
|
+
request.input(paramName, preparedValue);
|
|
908
632
|
}
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
try {
|
|
913
|
-
await transaction.rollback();
|
|
914
|
-
} catch {
|
|
915
|
-
}
|
|
916
|
-
throw error;
|
|
633
|
+
});
|
|
634
|
+
const deleteSql = `DELETE FROM ${tableName_} WHERE ${conditions.join(" AND ")}`;
|
|
635
|
+
await request.query(deleteSql);
|
|
917
636
|
}
|
|
637
|
+
await transaction.commit();
|
|
918
638
|
} catch (error$1) {
|
|
639
|
+
await transaction.rollback();
|
|
919
640
|
throw new error.MastraError(
|
|
920
641
|
{
|
|
921
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
642
|
+
id: storage.createStorageErrorId("MSSQL", "BATCH_DELETE", "FAILED"),
|
|
922
643
|
domain: error.ErrorDomain.STORAGE,
|
|
923
644
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
924
|
-
details: {
|
|
645
|
+
details: {
|
|
646
|
+
tableName,
|
|
647
|
+
numberOfRecords: keys.length
|
|
648
|
+
}
|
|
925
649
|
},
|
|
926
650
|
error$1
|
|
927
651
|
);
|
|
928
652
|
}
|
|
929
653
|
}
|
|
930
|
-
|
|
931
|
-
|
|
654
|
+
/**
|
|
655
|
+
* Create a new index on a table
|
|
656
|
+
*/
|
|
657
|
+
async createIndex(options) {
|
|
932
658
|
try {
|
|
933
|
-
const
|
|
934
|
-
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
|
|
659
|
+
const { name, table, columns, unique = false, where } = options;
|
|
660
|
+
const schemaName = this.schemaName || "dbo";
|
|
661
|
+
const fullTableName = getTableName({
|
|
662
|
+
indexName: table,
|
|
663
|
+
schemaName: getSchemaName(this.schemaName)
|
|
664
|
+
});
|
|
665
|
+
const indexNameSafe = utils.parseSqlIdentifier(name, "index name");
|
|
666
|
+
const checkRequest = this.pool.request();
|
|
667
|
+
checkRequest.input("indexName", indexNameSafe);
|
|
668
|
+
checkRequest.input("schemaName", schemaName);
|
|
669
|
+
checkRequest.input("tableName", table);
|
|
670
|
+
const indexExists = await checkRequest.query(`
|
|
671
|
+
SELECT 1 as found
|
|
672
|
+
FROM sys.indexes i
|
|
673
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
674
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
675
|
+
WHERE i.name = @indexName
|
|
676
|
+
AND s.name = @schemaName
|
|
677
|
+
AND t.name = @tableName
|
|
678
|
+
`);
|
|
679
|
+
if (indexExists.recordset && indexExists.recordset.length > 0) {
|
|
680
|
+
return;
|
|
938
681
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
682
|
+
const uniqueStr = unique ? "UNIQUE " : "";
|
|
683
|
+
const columnsStr = columns.map((col) => {
|
|
684
|
+
if (col.includes(" DESC") || col.includes(" ASC")) {
|
|
685
|
+
const [colName, ...modifiers] = col.split(" ");
|
|
686
|
+
if (!colName) {
|
|
687
|
+
throw new Error(`Invalid column specification: ${col}`);
|
|
688
|
+
}
|
|
689
|
+
return `[${utils.parseSqlIdentifier(colName, "column name")}] ${modifiers.join(" ")}`;
|
|
690
|
+
}
|
|
691
|
+
return `[${utils.parseSqlIdentifier(col, "column name")}]`;
|
|
692
|
+
}).join(", ");
|
|
693
|
+
const whereStr = where ? ` WHERE ${where}` : "";
|
|
694
|
+
const createIndexSql = `CREATE ${uniqueStr}INDEX [${indexNameSafe}] ON ${fullTableName} (${columnsStr})${whereStr}`;
|
|
695
|
+
await this.pool.request().query(createIndexSql);
|
|
946
696
|
} catch (error$1) {
|
|
947
|
-
|
|
697
|
+
throw new error.MastraError(
|
|
948
698
|
{
|
|
949
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
699
|
+
id: storage.createStorageErrorId("MSSQL", "INDEX_CREATE", "FAILED"),
|
|
950
700
|
domain: error.ErrorDomain.STORAGE,
|
|
951
701
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
952
|
-
details: {
|
|
702
|
+
details: {
|
|
703
|
+
indexName: options.name,
|
|
704
|
+
tableName: options.table
|
|
705
|
+
}
|
|
953
706
|
},
|
|
954
707
|
error$1
|
|
955
708
|
);
|
|
956
|
-
this.logger?.error?.(mastraError.toString());
|
|
957
|
-
this.logger?.trackException?.(mastraError);
|
|
958
|
-
throw mastraError;
|
|
959
709
|
}
|
|
960
710
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
...resource,
|
|
966
|
-
metadata: resource.metadata
|
|
967
|
-
}
|
|
968
|
-
});
|
|
969
|
-
return resource;
|
|
970
|
-
}
|
|
971
|
-
async updateResource({
|
|
972
|
-
resourceId,
|
|
973
|
-
workingMemory,
|
|
974
|
-
metadata
|
|
975
|
-
}) {
|
|
711
|
+
/**
|
|
712
|
+
* Drop an existing index
|
|
713
|
+
*/
|
|
714
|
+
async dropIndex(indexName) {
|
|
976
715
|
try {
|
|
977
|
-
const
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
...existingResource.metadata,
|
|
993
|
-
...metadata
|
|
994
|
-
},
|
|
995
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
996
|
-
};
|
|
997
|
-
const tableName = getTableName({ indexName: storage.TABLE_RESOURCES, schemaName: getSchemaName(this.schema) });
|
|
998
|
-
const updates = [];
|
|
999
|
-
const req = this.pool.request();
|
|
1000
|
-
if (workingMemory !== void 0) {
|
|
1001
|
-
updates.push("workingMemory = @workingMemory");
|
|
1002
|
-
req.input("workingMemory", workingMemory);
|
|
716
|
+
const schemaName = this.schemaName || "dbo";
|
|
717
|
+
const indexNameSafe = utils.parseSqlIdentifier(indexName, "index name");
|
|
718
|
+
const checkRequest = this.pool.request();
|
|
719
|
+
checkRequest.input("indexName", indexNameSafe);
|
|
720
|
+
checkRequest.input("schemaName", schemaName);
|
|
721
|
+
const result = await checkRequest.query(`
|
|
722
|
+
SELECT t.name as table_name
|
|
723
|
+
FROM sys.indexes i
|
|
724
|
+
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
725
|
+
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
726
|
+
WHERE i.name = @indexName
|
|
727
|
+
AND s.name = @schemaName
|
|
728
|
+
`);
|
|
729
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
730
|
+
return;
|
|
1003
731
|
}
|
|
1004
|
-
if (
|
|
1005
|
-
|
|
1006
|
-
|
|
732
|
+
if (result.recordset.length > 1) {
|
|
733
|
+
const tables = result.recordset.map((r) => r.table_name).join(", ");
|
|
734
|
+
throw new error.MastraError({
|
|
735
|
+
id: storage.createStorageErrorId("MSSQL", "INDEX", "AMBIGUOUS"),
|
|
736
|
+
domain: error.ErrorDomain.STORAGE,
|
|
737
|
+
category: error.ErrorCategory.USER,
|
|
738
|
+
text: `Index "${indexNameSafe}" exists on multiple tables (${tables}) in schema "${schemaName}". Please drop indexes manually or ensure unique index names.`
|
|
739
|
+
});
|
|
1007
740
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
741
|
+
const tableName = result.recordset[0].table_name;
|
|
742
|
+
const fullTableName = getTableName({
|
|
743
|
+
indexName: tableName,
|
|
744
|
+
schemaName: getSchemaName(this.schemaName)
|
|
745
|
+
});
|
|
746
|
+
const dropSql = `DROP INDEX [${indexNameSafe}] ON ${fullTableName}`;
|
|
747
|
+
await this.pool.request().query(dropSql);
|
|
1013
748
|
} catch (error$1) {
|
|
1014
|
-
|
|
749
|
+
throw new error.MastraError(
|
|
1015
750
|
{
|
|
1016
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
751
|
+
id: storage.createStorageErrorId("MSSQL", "INDEX_DROP", "FAILED"),
|
|
1017
752
|
domain: error.ErrorDomain.STORAGE,
|
|
1018
753
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
1019
|
-
details: {
|
|
754
|
+
details: {
|
|
755
|
+
indexName
|
|
756
|
+
}
|
|
1020
757
|
},
|
|
1021
758
|
error$1
|
|
1022
759
|
);
|
|
1023
|
-
this.logger?.error?.(mastraError.toString());
|
|
1024
|
-
this.logger?.trackException?.(mastraError);
|
|
1025
|
-
throw mastraError;
|
|
1026
760
|
}
|
|
1027
761
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
schema;
|
|
1033
|
-
constructor({
|
|
1034
|
-
pool,
|
|
1035
|
-
operations,
|
|
1036
|
-
schema
|
|
1037
|
-
}) {
|
|
1038
|
-
super();
|
|
1039
|
-
this.pool = pool;
|
|
1040
|
-
this.operations = operations;
|
|
1041
|
-
this.schema = schema;
|
|
1042
|
-
}
|
|
1043
|
-
get tracingStrategy() {
|
|
1044
|
-
return {
|
|
1045
|
-
preferred: "batch-with-updates",
|
|
1046
|
-
supported: ["batch-with-updates", "insert-only"]
|
|
1047
|
-
};
|
|
1048
|
-
}
|
|
1049
|
-
async createSpan(span) {
|
|
762
|
+
/**
|
|
763
|
+
* List indexes for a specific table or all tables
|
|
764
|
+
*/
|
|
765
|
+
async listIndexes(tableName) {
|
|
1050
766
|
try {
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
767
|
+
const schemaName = this.schemaName || "dbo";
|
|
768
|
+
let query;
|
|
769
|
+
const request = this.pool.request();
|
|
770
|
+
request.input("schemaName", schemaName);
|
|
771
|
+
if (tableName) {
|
|
772
|
+
query = `
|
|
773
|
+
SELECT
|
|
774
|
+
i.name as name,
|
|
775
|
+
o.name as [table],
|
|
776
|
+
i.is_unique as is_unique,
|
|
777
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
|
|
778
|
+
FROM sys.indexes i
|
|
779
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
780
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
781
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
782
|
+
WHERE sch.name = @schemaName
|
|
783
|
+
AND o.name = @tableName
|
|
784
|
+
AND i.name IS NOT NULL
|
|
785
|
+
GROUP BY i.name, o.name, i.is_unique
|
|
786
|
+
`;
|
|
787
|
+
request.input("tableName", tableName);
|
|
788
|
+
} else {
|
|
789
|
+
query = `
|
|
790
|
+
SELECT
|
|
791
|
+
i.name as name,
|
|
792
|
+
o.name as [table],
|
|
793
|
+
i.is_unique as is_unique,
|
|
794
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size
|
|
795
|
+
FROM sys.indexes i
|
|
796
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
797
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
798
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
799
|
+
WHERE sch.name = @schemaName
|
|
800
|
+
AND i.name IS NOT NULL
|
|
801
|
+
GROUP BY i.name, o.name, i.is_unique
|
|
802
|
+
`;
|
|
803
|
+
}
|
|
804
|
+
const result = await request.query(query);
|
|
805
|
+
const indexes = [];
|
|
806
|
+
for (const row of result.recordset) {
|
|
807
|
+
const colRequest = this.pool.request();
|
|
808
|
+
colRequest.input("indexName", row.name);
|
|
809
|
+
colRequest.input("schemaName", schemaName);
|
|
810
|
+
const colResult = await colRequest.query(`
|
|
811
|
+
SELECT c.name as column_name
|
|
812
|
+
FROM sys.indexes i
|
|
813
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
814
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
815
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
816
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
817
|
+
WHERE i.name = @indexName
|
|
818
|
+
AND s.name = @schemaName
|
|
819
|
+
ORDER BY ic.key_ordinal
|
|
820
|
+
`);
|
|
821
|
+
indexes.push({
|
|
822
|
+
name: row.name,
|
|
823
|
+
table: row.table,
|
|
824
|
+
columns: colResult.recordset.map((c) => c.column_name),
|
|
825
|
+
unique: row.is_unique || false,
|
|
826
|
+
size: row.size || "0 MB",
|
|
827
|
+
definition: ""
|
|
828
|
+
// MSSQL doesn't store definition like PG
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
return indexes;
|
|
1060
832
|
} catch (error$1) {
|
|
1061
833
|
throw new error.MastraError(
|
|
1062
834
|
{
|
|
1063
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
835
|
+
id: storage.createStorageErrorId("MSSQL", "INDEX_LIST", "FAILED"),
|
|
1064
836
|
domain: error.ErrorDomain.STORAGE,
|
|
1065
|
-
category: error.ErrorCategory.
|
|
1066
|
-
details: {
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
spanType: span.spanType,
|
|
1070
|
-
spanName: span.name
|
|
1071
|
-
}
|
|
837
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
838
|
+
details: tableName ? {
|
|
839
|
+
tableName
|
|
840
|
+
} : {}
|
|
1072
841
|
},
|
|
1073
842
|
error$1
|
|
1074
843
|
);
|
|
1075
844
|
}
|
|
1076
845
|
}
|
|
1077
|
-
|
|
846
|
+
/**
|
|
847
|
+
* Get detailed statistics for a specific index
|
|
848
|
+
*/
|
|
849
|
+
async describeIndex(indexName) {
|
|
1078
850
|
try {
|
|
1079
|
-
const
|
|
1080
|
-
indexName: storage.TABLE_SPANS,
|
|
1081
|
-
schemaName: getSchemaName(this.schema)
|
|
1082
|
-
});
|
|
851
|
+
const schemaName = this.schemaName || "dbo";
|
|
1083
852
|
const request = this.pool.request();
|
|
1084
|
-
request.input("
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
853
|
+
request.input("indexName", indexName);
|
|
854
|
+
request.input("schemaName", schemaName);
|
|
855
|
+
const query = `
|
|
856
|
+
SELECT
|
|
857
|
+
i.name as name,
|
|
858
|
+
o.name as [table],
|
|
859
|
+
i.is_unique as is_unique,
|
|
860
|
+
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size,
|
|
861
|
+
i.type_desc as method,
|
|
862
|
+
ISNULL(us.user_scans, 0) as scans,
|
|
863
|
+
ISNULL(us.user_seeks + us.user_scans, 0) as tuples_read,
|
|
864
|
+
ISNULL(us.user_lookups, 0) as tuples_fetched
|
|
865
|
+
FROM sys.indexes i
|
|
866
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
867
|
+
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
868
|
+
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
869
|
+
LEFT JOIN sys.dm_db_index_usage_stats us ON i.object_id = us.object_id AND i.index_id = us.index_id
|
|
870
|
+
WHERE i.name = @indexName
|
|
871
|
+
AND sch.name = @schemaName
|
|
872
|
+
GROUP BY i.name, o.name, i.is_unique, i.type_desc, us.user_seeks, us.user_scans, us.user_lookups
|
|
873
|
+
`;
|
|
874
|
+
const result = await request.query(query);
|
|
1094
875
|
if (!result.recordset || result.recordset.length === 0) {
|
|
1095
|
-
|
|
876
|
+
throw new Error(`Index "${indexName}" not found in schema "${schemaName}"`);
|
|
1096
877
|
}
|
|
878
|
+
const row = result.recordset[0];
|
|
879
|
+
const colRequest = this.pool.request();
|
|
880
|
+
colRequest.input("indexName", indexName);
|
|
881
|
+
colRequest.input("schemaName", schemaName);
|
|
882
|
+
const colResult = await colRequest.query(`
|
|
883
|
+
SELECT c.name as column_name
|
|
884
|
+
FROM sys.indexes i
|
|
885
|
+
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
886
|
+
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
887
|
+
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
888
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
889
|
+
WHERE i.name = @indexName
|
|
890
|
+
AND s.name = @schemaName
|
|
891
|
+
ORDER BY ic.key_ordinal
|
|
892
|
+
`);
|
|
1097
893
|
return {
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
)
|
|
894
|
+
name: row.name,
|
|
895
|
+
table: row.table,
|
|
896
|
+
columns: colResult.recordset.map((c) => c.column_name),
|
|
897
|
+
unique: row.is_unique || false,
|
|
898
|
+
size: row.size || "0 MB",
|
|
899
|
+
definition: "",
|
|
900
|
+
method: row.method?.toLowerCase() || "nonclustered",
|
|
901
|
+
scans: Number(row.scans) || 0,
|
|
902
|
+
tuples_read: Number(row.tuples_read) || 0,
|
|
903
|
+
tuples_fetched: Number(row.tuples_fetched) || 0
|
|
1105
904
|
};
|
|
1106
905
|
} catch (error$1) {
|
|
1107
906
|
throw new error.MastraError(
|
|
1108
907
|
{
|
|
1109
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
908
|
+
id: storage.createStorageErrorId("MSSQL", "INDEX_DESCRIBE", "FAILED"),
|
|
1110
909
|
domain: error.ErrorDomain.STORAGE,
|
|
1111
|
-
category: error.ErrorCategory.
|
|
910
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1112
911
|
details: {
|
|
1113
|
-
|
|
912
|
+
indexName
|
|
1114
913
|
}
|
|
1115
914
|
},
|
|
1116
915
|
error$1
|
|
1117
916
|
);
|
|
1118
917
|
}
|
|
1119
918
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
919
|
+
/**
|
|
920
|
+
* Returns definitions for automatic performance indexes
|
|
921
|
+
* IMPORTANT: Uses seq_id DESC instead of createdAt DESC for MSSQL due to millisecond accuracy limitations
|
|
922
|
+
* NOTE: Using NVARCHAR(400) for text columns (800 bytes) leaves room for composite indexes
|
|
923
|
+
*/
|
|
924
|
+
getAutomaticIndexDefinitions() {
|
|
925
|
+
const schemaPrefix = this.schemaName ? `${this.schemaName}_` : "";
|
|
926
|
+
return [
|
|
927
|
+
// Composite indexes for optimal filtering + sorting performance
|
|
928
|
+
// NVARCHAR(400) = 800 bytes, plus BIGINT (8 bytes) = 808 bytes total (under 900-byte limit)
|
|
929
|
+
{
|
|
930
|
+
name: `${schemaPrefix}mastra_threads_resourceid_seqid_idx`,
|
|
931
|
+
table: storage.TABLE_THREADS,
|
|
932
|
+
columns: ["resourceId", "seq_id DESC"]
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
name: `${schemaPrefix}mastra_messages_thread_id_seqid_idx`,
|
|
936
|
+
table: storage.TABLE_MESSAGES,
|
|
937
|
+
columns: ["thread_id", "seq_id DESC"]
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
name: `${schemaPrefix}mastra_traces_name_seqid_idx`,
|
|
941
|
+
table: storage.TABLE_TRACES,
|
|
942
|
+
columns: ["name", "seq_id DESC"]
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
name: `${schemaPrefix}mastra_scores_trace_id_span_id_seqid_idx`,
|
|
946
|
+
table: storage.TABLE_SCORERS,
|
|
947
|
+
columns: ["traceId", "spanId", "seq_id DESC"]
|
|
948
|
+
},
|
|
949
|
+
// Spans indexes for optimal trace querying
|
|
950
|
+
{
|
|
951
|
+
name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
|
|
952
|
+
table: storage.TABLE_SPANS,
|
|
953
|
+
columns: ["traceId", "startedAt DESC"]
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
|
|
957
|
+
table: storage.TABLE_SPANS,
|
|
958
|
+
columns: ["parentSpanId", "startedAt DESC"]
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
name: `${schemaPrefix}mastra_ai_spans_name_idx`,
|
|
962
|
+
table: storage.TABLE_SPANS,
|
|
963
|
+
columns: ["name"]
|
|
964
|
+
},
|
|
965
|
+
{
|
|
966
|
+
name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
|
|
967
|
+
table: storage.TABLE_SPANS,
|
|
968
|
+
columns: ["spanType", "startedAt DESC"]
|
|
1129
969
|
}
|
|
1130
|
-
|
|
1131
|
-
|
|
970
|
+
];
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Creates automatic indexes for optimal query performance
|
|
974
|
+
* Uses getAutomaticIndexDefinitions() to determine which indexes to create
|
|
975
|
+
*/
|
|
976
|
+
async createAutomaticIndexes() {
|
|
977
|
+
try {
|
|
978
|
+
const indexes = this.getAutomaticIndexDefinitions();
|
|
979
|
+
for (const indexOptions of indexes) {
|
|
980
|
+
try {
|
|
981
|
+
await this.createIndex(indexOptions);
|
|
982
|
+
} catch (error) {
|
|
983
|
+
this.logger?.warn?.(`Failed to create index ${indexOptions.name}:`, error);
|
|
984
|
+
}
|
|
1132
985
|
}
|
|
1133
|
-
await this.operations.update({
|
|
1134
|
-
tableName: storage.TABLE_SPANS,
|
|
1135
|
-
keys: { spanId, traceId },
|
|
1136
|
-
data
|
|
1137
|
-
});
|
|
1138
986
|
} catch (error$1) {
|
|
1139
987
|
throw new error.MastraError(
|
|
1140
988
|
{
|
|
1141
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
989
|
+
id: storage.createStorageErrorId("MSSQL", "CREATE_PERFORMANCE_INDEXES", "FAILED"),
|
|
1142
990
|
domain: error.ErrorDomain.STORAGE,
|
|
1143
|
-
category: error.ErrorCategory.
|
|
1144
|
-
details: {
|
|
1145
|
-
spanId,
|
|
1146
|
-
traceId
|
|
1147
|
-
}
|
|
991
|
+
category: error.ErrorCategory.THIRD_PARTY
|
|
1148
992
|
},
|
|
1149
993
|
error$1
|
|
1150
994
|
);
|
|
1151
995
|
}
|
|
1152
996
|
}
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
997
|
+
};
|
|
998
|
+
function getSchemaName2(schema) {
|
|
999
|
+
return schema ? `[${utils.parseSqlIdentifier(schema, "schema name")}]` : void 0;
|
|
1000
|
+
}
|
|
1001
|
+
function getTableName2({ indexName, schemaName }) {
|
|
1002
|
+
const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
|
|
1003
|
+
const quotedIndexName = `[${parsedIndexName}]`;
|
|
1004
|
+
const quotedSchemaName = schemaName;
|
|
1005
|
+
return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
|
|
1006
|
+
}
|
|
1007
|
+
function buildDateRangeFilter(dateRange, fieldName) {
|
|
1008
|
+
const filters = {};
|
|
1009
|
+
if (dateRange?.start) {
|
|
1010
|
+
filters[`${fieldName}_gte`] = dateRange.start;
|
|
1011
|
+
}
|
|
1012
|
+
if (dateRange?.end) {
|
|
1013
|
+
filters[`${fieldName}_lte`] = dateRange.end;
|
|
1014
|
+
}
|
|
1015
|
+
return filters;
|
|
1016
|
+
}
|
|
1017
|
+
function isInOperator(value) {
|
|
1018
|
+
return typeof value === "object" && value !== null && "$in" in value && Array.isArray(value.$in);
|
|
1019
|
+
}
|
|
1020
|
+
function prepareWhereClause(filters, _schema) {
|
|
1021
|
+
const conditions = [];
|
|
1022
|
+
const params = {};
|
|
1023
|
+
let paramIndex = 1;
|
|
1024
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
1025
|
+
if (value === void 0) return;
|
|
1026
|
+
if (key.endsWith("_gte")) {
|
|
1027
|
+
const paramName = `p${paramIndex++}`;
|
|
1028
|
+
const fieldName = key.slice(0, -4);
|
|
1029
|
+
conditions.push(`[${utils.parseSqlIdentifier(fieldName, "field name")}] >= @${paramName}`);
|
|
1030
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
1031
|
+
} else if (key.endsWith("_lte")) {
|
|
1032
|
+
const paramName = `p${paramIndex++}`;
|
|
1033
|
+
const fieldName = key.slice(0, -4);
|
|
1034
|
+
conditions.push(`[${utils.parseSqlIdentifier(fieldName, "field name")}] <= @${paramName}`);
|
|
1035
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
1036
|
+
} else if (value === null) {
|
|
1037
|
+
conditions.push(`[${utils.parseSqlIdentifier(key, "field name")}] IS NULL`);
|
|
1038
|
+
} else if (isInOperator(value)) {
|
|
1039
|
+
const inValues = value.$in;
|
|
1040
|
+
if (inValues.length === 0) {
|
|
1041
|
+
conditions.push("1 = 0");
|
|
1042
|
+
} else if (inValues.length === 1) {
|
|
1043
|
+
const paramName = `p${paramIndex++}`;
|
|
1044
|
+
conditions.push(`[${utils.parseSqlIdentifier(key, "field name")}] = @${paramName}`);
|
|
1045
|
+
params[paramName] = inValues[0] instanceof Date ? inValues[0].toISOString() : inValues[0];
|
|
1176
1046
|
} else {
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
text: `Cannot filter by entity type: ${entityType}`
|
|
1185
|
-
});
|
|
1186
|
-
throw error$1;
|
|
1047
|
+
const inParamNames = [];
|
|
1048
|
+
for (const item of inValues) {
|
|
1049
|
+
const paramName = `p${paramIndex++}`;
|
|
1050
|
+
inParamNames.push(`@${paramName}`);
|
|
1051
|
+
params[paramName] = item instanceof Date ? item.toISOString() : item;
|
|
1052
|
+
}
|
|
1053
|
+
conditions.push(`[${utils.parseSqlIdentifier(key, "field name")}] IN (${inParamNames.join(", ")})`);
|
|
1187
1054
|
}
|
|
1188
|
-
|
|
1189
|
-
if (
|
|
1190
|
-
|
|
1055
|
+
} else if (Array.isArray(value)) {
|
|
1056
|
+
if (value.length === 0) {
|
|
1057
|
+
conditions.push("1 = 0");
|
|
1058
|
+
} else if (value.length === 1) {
|
|
1059
|
+
const paramName = `p${paramIndex++}`;
|
|
1060
|
+
conditions.push(`[${utils.parseSqlIdentifier(key, "field name")}] = @${paramName}`);
|
|
1061
|
+
params[paramName] = value[0] instanceof Date ? value[0].toISOString() : value[0];
|
|
1191
1062
|
} else {
|
|
1192
|
-
|
|
1063
|
+
const inParamNames = [];
|
|
1064
|
+
for (const item of value) {
|
|
1065
|
+
const paramName = `p${paramIndex++}`;
|
|
1066
|
+
inParamNames.push(`@${paramName}`);
|
|
1067
|
+
params[paramName] = item instanceof Date ? item.toISOString() : item;
|
|
1068
|
+
}
|
|
1069
|
+
conditions.push(`[${utils.parseSqlIdentifier(key, "field name")}] IN (${inParamNames.join(", ")})`);
|
|
1193
1070
|
}
|
|
1194
|
-
|
|
1071
|
+
} else {
|
|
1072
|
+
const paramName = `p${paramIndex++}`;
|
|
1073
|
+
conditions.push(`[${utils.parseSqlIdentifier(key, "field name")}] = @${paramName}`);
|
|
1074
|
+
params[paramName] = value instanceof Date ? value.toISOString() : value;
|
|
1195
1075
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
hasMore: false
|
|
1216
|
-
},
|
|
1217
|
-
spans: []
|
|
1218
|
-
};
|
|
1076
|
+
});
|
|
1077
|
+
return {
|
|
1078
|
+
sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
|
|
1079
|
+
params
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
function transformFromSqlRow({
|
|
1083
|
+
tableName,
|
|
1084
|
+
sqlRow
|
|
1085
|
+
}) {
|
|
1086
|
+
const schema = storage.TABLE_SCHEMAS[tableName];
|
|
1087
|
+
const result = {};
|
|
1088
|
+
Object.entries(sqlRow).forEach(([key, value]) => {
|
|
1089
|
+
const columnSchema = schema?.[key];
|
|
1090
|
+
if (columnSchema?.type === "jsonb" && typeof value === "string") {
|
|
1091
|
+
try {
|
|
1092
|
+
result[key] = JSON.parse(value);
|
|
1093
|
+
} catch {
|
|
1094
|
+
result[key] = value;
|
|
1219
1095
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
);
|
|
1229
|
-
const spans = dataResult.recordset.map(
|
|
1230
|
-
(row) => transformFromSqlRow({
|
|
1231
|
-
tableName: storage.TABLE_SPANS,
|
|
1232
|
-
sqlRow: row
|
|
1233
|
-
})
|
|
1234
|
-
);
|
|
1235
|
-
return {
|
|
1236
|
-
pagination: {
|
|
1237
|
-
total,
|
|
1238
|
-
page,
|
|
1239
|
-
perPage,
|
|
1240
|
-
hasMore: (page + 1) * perPage < total
|
|
1241
|
-
},
|
|
1242
|
-
spans
|
|
1243
|
-
};
|
|
1244
|
-
} catch (error$1) {
|
|
1245
|
-
throw new error.MastraError(
|
|
1246
|
-
{
|
|
1247
|
-
id: storage.createStorageErrorId("MSSQL", "GET_TRACES_PAGINATED", "FAILED"),
|
|
1248
|
-
domain: error.ErrorDomain.STORAGE,
|
|
1249
|
-
category: error.ErrorCategory.USER
|
|
1250
|
-
},
|
|
1251
|
-
error$1
|
|
1252
|
-
);
|
|
1096
|
+
} else if (columnSchema?.type === "timestamp" && value && typeof value === "string") {
|
|
1097
|
+
result[key] = new Date(value);
|
|
1098
|
+
} else if (columnSchema?.type === "timestamp" && value instanceof Date) {
|
|
1099
|
+
result[key] = value;
|
|
1100
|
+
} else if (columnSchema?.type === "boolean") {
|
|
1101
|
+
result[key] = Boolean(value);
|
|
1102
|
+
} else {
|
|
1103
|
+
result[key] = value;
|
|
1253
1104
|
}
|
|
1105
|
+
});
|
|
1106
|
+
return result;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// src/storage/domains/memory/index.ts
|
|
1110
|
+
var MemoryMSSQL = class extends storage.MemoryStorage {
|
|
1111
|
+
pool;
|
|
1112
|
+
schema;
|
|
1113
|
+
db;
|
|
1114
|
+
needsConnect;
|
|
1115
|
+
_parseAndFormatMessages(messages, format) {
|
|
1116
|
+
const messagesWithParsedContent = messages.map((message) => {
|
|
1117
|
+
if (typeof message.content === "string") {
|
|
1118
|
+
try {
|
|
1119
|
+
return { ...message, content: JSON.parse(message.content) };
|
|
1120
|
+
} catch {
|
|
1121
|
+
return message;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
return message;
|
|
1125
|
+
});
|
|
1126
|
+
const cleanMessages = messagesWithParsedContent.map(({ seq_id, ...rest }) => rest);
|
|
1127
|
+
const list = new agent.MessageList().add(cleanMessages, "memory");
|
|
1128
|
+
return format === "v2" ? list.get.all.db() : list.get.all.v1();
|
|
1254
1129
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1130
|
+
constructor(config) {
|
|
1131
|
+
super();
|
|
1132
|
+
const { pool, db, schema, needsConnect } = resolveMssqlConfig(config);
|
|
1133
|
+
this.pool = pool;
|
|
1134
|
+
this.schema = schema;
|
|
1135
|
+
this.db = db;
|
|
1136
|
+
this.needsConnect = needsConnect;
|
|
1137
|
+
}
|
|
1138
|
+
async init() {
|
|
1139
|
+
if (this.needsConnect) {
|
|
1140
|
+
await this.pool.connect();
|
|
1141
|
+
this.needsConnect = false;
|
|
1258
1142
|
}
|
|
1143
|
+
await this.db.createTable({ tableName: storage.TABLE_THREADS, schema: storage.TABLE_SCHEMAS[storage.TABLE_THREADS] });
|
|
1144
|
+
await this.db.createTable({ tableName: storage.TABLE_MESSAGES, schema: storage.TABLE_SCHEMAS[storage.TABLE_MESSAGES] });
|
|
1145
|
+
await this.db.createTable({ tableName: storage.TABLE_RESOURCES, schema: storage.TABLE_SCHEMAS[storage.TABLE_RESOURCES] });
|
|
1146
|
+
}
|
|
1147
|
+
async dangerouslyClearAll() {
|
|
1148
|
+
await this.db.clearTable({ tableName: storage.TABLE_MESSAGES });
|
|
1149
|
+
await this.db.clearTable({ tableName: storage.TABLE_THREADS });
|
|
1150
|
+
await this.db.clearTable({ tableName: storage.TABLE_RESOURCES });
|
|
1151
|
+
}
|
|
1152
|
+
async getThreadById({ threadId }) {
|
|
1259
1153
|
try {
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
})
|
|
1154
|
+
const sql5 = `SELECT
|
|
1155
|
+
id,
|
|
1156
|
+
[resourceId],
|
|
1157
|
+
title,
|
|
1158
|
+
metadata,
|
|
1159
|
+
[createdAt],
|
|
1160
|
+
[updatedAt]
|
|
1161
|
+
FROM ${getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.schema) })}
|
|
1162
|
+
WHERE id = @threadId`;
|
|
1163
|
+
const request = this.pool.request();
|
|
1164
|
+
request.input("threadId", threadId);
|
|
1165
|
+
const resultSet = await request.query(sql5);
|
|
1166
|
+
const thread = resultSet.recordset[0] || null;
|
|
1167
|
+
if (!thread) {
|
|
1168
|
+
return null;
|
|
1169
|
+
}
|
|
1170
|
+
return {
|
|
1171
|
+
...thread,
|
|
1172
|
+
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1173
|
+
createdAt: thread.createdAt,
|
|
1174
|
+
updatedAt: thread.updatedAt
|
|
1175
|
+
};
|
|
1268
1176
|
} catch (error$1) {
|
|
1269
1177
|
throw new error.MastraError(
|
|
1270
1178
|
{
|
|
1271
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
1179
|
+
id: storage.createStorageErrorId("MSSQL", "GET_THREAD_BY_ID", "FAILED"),
|
|
1272
1180
|
domain: error.ErrorDomain.STORAGE,
|
|
1273
|
-
category: error.ErrorCategory.
|
|
1181
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1274
1182
|
details: {
|
|
1275
|
-
|
|
1183
|
+
threadId
|
|
1276
1184
|
}
|
|
1277
1185
|
},
|
|
1278
1186
|
error$1
|
|
1279
1187
|
);
|
|
1280
1188
|
}
|
|
1281
1189
|
}
|
|
1282
|
-
async
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
processedData.startedAt = processedData.startedAt.toISOString();
|
|
1190
|
+
async listThreadsByResourceId(args) {
|
|
1191
|
+
const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
|
|
1192
|
+
if (page < 0) {
|
|
1193
|
+
throw new error.MastraError({
|
|
1194
|
+
id: storage.createStorageErrorId("MSSQL", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
|
|
1195
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1196
|
+
category: error.ErrorCategory.USER,
|
|
1197
|
+
text: "Page number must be non-negative",
|
|
1198
|
+
details: {
|
|
1199
|
+
resourceId,
|
|
1200
|
+
page
|
|
1294
1201
|
}
|
|
1295
|
-
return {
|
|
1296
|
-
keys: { spanId, traceId },
|
|
1297
|
-
data: processedData
|
|
1298
|
-
};
|
|
1299
|
-
});
|
|
1300
|
-
await this.operations.batchUpdate({
|
|
1301
|
-
tableName: storage.TABLE_SPANS,
|
|
1302
|
-
updates
|
|
1303
1202
|
});
|
|
1304
|
-
} catch (error$1) {
|
|
1305
|
-
throw new error.MastraError(
|
|
1306
|
-
{
|
|
1307
|
-
id: storage.createStorageErrorId("MSSQL", "BATCH_UPDATE_SPANS", "FAILED"),
|
|
1308
|
-
domain: error.ErrorDomain.STORAGE,
|
|
1309
|
-
category: error.ErrorCategory.USER,
|
|
1310
|
-
details: {
|
|
1311
|
-
count: args.records.length
|
|
1312
|
-
}
|
|
1313
|
-
},
|
|
1314
|
-
error$1
|
|
1315
|
-
);
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
async batchDeleteTraces(args) {
|
|
1319
|
-
if (!args.traceIds || args.traceIds.length === 0) {
|
|
1320
|
-
return;
|
|
1321
1203
|
}
|
|
1204
|
+
const perPage = storage.normalizePerPage(perPageInput, 100);
|
|
1205
|
+
const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
|
|
1206
|
+
const { field, direction } = this.parseOrderBy(orderBy);
|
|
1322
1207
|
try {
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1208
|
+
const baseQuery = `FROM ${getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.schema) })} WHERE [resourceId] = @resourceId`;
|
|
1209
|
+
const countQuery = `SELECT COUNT(*) as count ${baseQuery}`;
|
|
1210
|
+
const countRequest = this.pool.request();
|
|
1211
|
+
countRequest.input("resourceId", resourceId);
|
|
1212
|
+
const countResult = await countRequest.query(countQuery);
|
|
1213
|
+
const total = parseInt(countResult.recordset[0]?.count ?? "0", 10);
|
|
1214
|
+
if (total === 0) {
|
|
1215
|
+
return {
|
|
1216
|
+
threads: [],
|
|
1217
|
+
total: 0,
|
|
1218
|
+
page,
|
|
1219
|
+
perPage: perPageForResponse,
|
|
1220
|
+
hasMore: false
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
const orderByField = field === "createdAt" ? "[createdAt]" : "[updatedAt]";
|
|
1224
|
+
const dir = (direction || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";
|
|
1225
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
1226
|
+
const dataQuery = `SELECT id, [resourceId], title, metadata, [createdAt], [updatedAt] ${baseQuery} ORDER BY ${orderByField} ${dir} OFFSET @offset ROWS FETCH NEXT @perPage ROWS ONLY`;
|
|
1227
|
+
const dataRequest = this.pool.request();
|
|
1228
|
+
dataRequest.input("resourceId", resourceId);
|
|
1229
|
+
dataRequest.input("offset", offset);
|
|
1230
|
+
if (limitValue > 2147483647) {
|
|
1231
|
+
dataRequest.input("perPage", sql__default.default.BigInt, limitValue);
|
|
1232
|
+
} else {
|
|
1233
|
+
dataRequest.input("perPage", limitValue);
|
|
1234
|
+
}
|
|
1235
|
+
const rowsResult = await dataRequest.query(dataQuery);
|
|
1236
|
+
const rows = rowsResult.recordset || [];
|
|
1237
|
+
const threads = rows.map((thread) => ({
|
|
1238
|
+
...thread,
|
|
1239
|
+
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1240
|
+
createdAt: thread.createdAt,
|
|
1241
|
+
updatedAt: thread.updatedAt
|
|
1242
|
+
}));
|
|
1243
|
+
return {
|
|
1244
|
+
threads,
|
|
1245
|
+
total,
|
|
1246
|
+
page,
|
|
1247
|
+
perPage: perPageForResponse,
|
|
1248
|
+
hasMore: perPageInput === false ? false : offset + perPage < total
|
|
1249
|
+
};
|
|
1328
1250
|
} catch (error$1) {
|
|
1329
|
-
|
|
1251
|
+
const mastraError = new error.MastraError(
|
|
1330
1252
|
{
|
|
1331
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
1253
|
+
id: storage.createStorageErrorId("MSSQL", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
|
|
1332
1254
|
domain: error.ErrorDomain.STORAGE,
|
|
1333
|
-
category: error.ErrorCategory.
|
|
1255
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1334
1256
|
details: {
|
|
1335
|
-
|
|
1257
|
+
resourceId,
|
|
1258
|
+
page
|
|
1336
1259
|
}
|
|
1337
1260
|
},
|
|
1338
1261
|
error$1
|
|
1339
1262
|
);
|
|
1263
|
+
this.logger?.error?.(mastraError.toString());
|
|
1264
|
+
this.logger?.trackException?.(mastraError);
|
|
1265
|
+
return {
|
|
1266
|
+
threads: [],
|
|
1267
|
+
total: 0,
|
|
1268
|
+
page,
|
|
1269
|
+
perPage: perPageForResponse,
|
|
1270
|
+
hasMore: false
|
|
1271
|
+
};
|
|
1340
1272
|
}
|
|
1341
1273
|
}
|
|
1342
|
-
}
|
|
1343
|
-
var StoreOperationsMSSQL = class extends storage.StoreOperations {
|
|
1344
|
-
pool;
|
|
1345
|
-
schemaName;
|
|
1346
|
-
setupSchemaPromise = null;
|
|
1347
|
-
schemaSetupComplete = void 0;
|
|
1348
|
-
getSqlType(type, isPrimaryKey = false, useLargeStorage = false) {
|
|
1349
|
-
switch (type) {
|
|
1350
|
-
case "text":
|
|
1351
|
-
if (useLargeStorage) {
|
|
1352
|
-
return "NVARCHAR(MAX)";
|
|
1353
|
-
}
|
|
1354
|
-
return isPrimaryKey ? "NVARCHAR(255)" : "NVARCHAR(400)";
|
|
1355
|
-
case "timestamp":
|
|
1356
|
-
return "DATETIME2(7)";
|
|
1357
|
-
case "uuid":
|
|
1358
|
-
return "UNIQUEIDENTIFIER";
|
|
1359
|
-
case "jsonb":
|
|
1360
|
-
return "NVARCHAR(MAX)";
|
|
1361
|
-
case "integer":
|
|
1362
|
-
return "INT";
|
|
1363
|
-
case "bigint":
|
|
1364
|
-
return "BIGINT";
|
|
1365
|
-
case "float":
|
|
1366
|
-
return "FLOAT";
|
|
1367
|
-
case "boolean":
|
|
1368
|
-
return "BIT";
|
|
1369
|
-
default:
|
|
1370
|
-
throw new error.MastraError({
|
|
1371
|
-
id: storage.createStorageErrorId("MSSQL", "TYPE", "NOT_SUPPORTED"),
|
|
1372
|
-
domain: error.ErrorDomain.STORAGE,
|
|
1373
|
-
category: error.ErrorCategory.THIRD_PARTY
|
|
1374
|
-
});
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
constructor({ pool, schemaName }) {
|
|
1378
|
-
super();
|
|
1379
|
-
this.pool = pool;
|
|
1380
|
-
this.schemaName = schemaName;
|
|
1381
|
-
}
|
|
1382
|
-
async hasColumn(table, column) {
|
|
1383
|
-
const schema = this.schemaName || "dbo";
|
|
1384
|
-
const request = this.pool.request();
|
|
1385
|
-
request.input("schema", schema);
|
|
1386
|
-
request.input("table", table);
|
|
1387
|
-
request.input("column", column);
|
|
1388
|
-
request.input("columnLower", column.toLowerCase());
|
|
1389
|
-
const result = await request.query(
|
|
1390
|
-
`SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND (COLUMN_NAME = @column OR COLUMN_NAME = @columnLower)`
|
|
1391
|
-
);
|
|
1392
|
-
return result.recordset.length > 0;
|
|
1393
|
-
}
|
|
1394
|
-
async setupSchema() {
|
|
1395
|
-
if (!this.schemaName || this.schemaSetupComplete) {
|
|
1396
|
-
return;
|
|
1397
|
-
}
|
|
1398
|
-
if (!this.setupSchemaPromise) {
|
|
1399
|
-
this.setupSchemaPromise = (async () => {
|
|
1400
|
-
try {
|
|
1401
|
-
const checkRequest = this.pool.request();
|
|
1402
|
-
checkRequest.input("schemaName", this.schemaName);
|
|
1403
|
-
const checkResult = await checkRequest.query(`
|
|
1404
|
-
SELECT 1 AS found FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @schemaName
|
|
1405
|
-
`);
|
|
1406
|
-
const schemaExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
1407
|
-
if (!schemaExists) {
|
|
1408
|
-
try {
|
|
1409
|
-
await this.pool.request().query(`CREATE SCHEMA [${this.schemaName}]`);
|
|
1410
|
-
this.logger?.info?.(`Schema "${this.schemaName}" created successfully`);
|
|
1411
|
-
} catch (error) {
|
|
1412
|
-
this.logger?.error?.(`Failed to create schema "${this.schemaName}"`, { error });
|
|
1413
|
-
throw new Error(
|
|
1414
|
-
`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.`
|
|
1415
|
-
);
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
this.schemaSetupComplete = true;
|
|
1419
|
-
this.logger?.debug?.(`Schema "${this.schemaName}" is ready for use`);
|
|
1420
|
-
} catch (error) {
|
|
1421
|
-
this.schemaSetupComplete = void 0;
|
|
1422
|
-
this.setupSchemaPromise = null;
|
|
1423
|
-
throw error;
|
|
1424
|
-
} finally {
|
|
1425
|
-
this.setupSchemaPromise = null;
|
|
1426
|
-
}
|
|
1427
|
-
})();
|
|
1428
|
-
}
|
|
1429
|
-
await this.setupSchemaPromise;
|
|
1430
|
-
}
|
|
1431
|
-
async insert({
|
|
1432
|
-
tableName,
|
|
1433
|
-
record,
|
|
1434
|
-
transaction
|
|
1435
|
-
}) {
|
|
1274
|
+
async saveThread({ thread }) {
|
|
1436
1275
|
try {
|
|
1437
|
-
const
|
|
1438
|
-
const
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1276
|
+
const table = getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.schema) });
|
|
1277
|
+
const mergeSql = `MERGE INTO ${table} WITH (HOLDLOCK) AS target
|
|
1278
|
+
USING (SELECT @id AS id) AS source
|
|
1279
|
+
ON (target.id = source.id)
|
|
1280
|
+
WHEN MATCHED THEN
|
|
1281
|
+
UPDATE SET
|
|
1282
|
+
[resourceId] = @resourceId,
|
|
1283
|
+
title = @title,
|
|
1284
|
+
metadata = @metadata,
|
|
1285
|
+
[updatedAt] = @updatedAt
|
|
1286
|
+
WHEN NOT MATCHED THEN
|
|
1287
|
+
INSERT (id, [resourceId], title, metadata, [createdAt], [updatedAt])
|
|
1288
|
+
VALUES (@id, @resourceId, @title, @metadata, @createdAt, @updatedAt);`;
|
|
1289
|
+
const req = this.pool.request();
|
|
1290
|
+
req.input("id", thread.id);
|
|
1291
|
+
req.input("resourceId", thread.resourceId);
|
|
1292
|
+
req.input("title", thread.title);
|
|
1293
|
+
const metadata = thread.metadata ? JSON.stringify(thread.metadata) : null;
|
|
1294
|
+
if (metadata === null) {
|
|
1295
|
+
req.input("metadata", sql__default.default.NVarChar, null);
|
|
1296
|
+
} else {
|
|
1297
|
+
req.input("metadata", metadata);
|
|
1298
|
+
}
|
|
1299
|
+
req.input("createdAt", sql__default.default.DateTime2, thread.createdAt);
|
|
1300
|
+
req.input("updatedAt", sql__default.default.DateTime2, thread.updatedAt);
|
|
1301
|
+
await req.query(mergeSql);
|
|
1302
|
+
return thread;
|
|
1454
1303
|
} catch (error$1) {
|
|
1455
1304
|
throw new error.MastraError(
|
|
1456
1305
|
{
|
|
1457
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
1306
|
+
id: storage.createStorageErrorId("MSSQL", "SAVE_THREAD", "FAILED"),
|
|
1458
1307
|
domain: error.ErrorDomain.STORAGE,
|
|
1459
1308
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
1460
1309
|
details: {
|
|
1461
|
-
|
|
1310
|
+
threadId: thread.id
|
|
1462
1311
|
}
|
|
1463
1312
|
},
|
|
1464
1313
|
error$1
|
|
1465
1314
|
);
|
|
1466
1315
|
}
|
|
1467
1316
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1317
|
+
/**
|
|
1318
|
+
* Updates a thread's title and metadata, merging with existing metadata. Returns the updated thread.
|
|
1319
|
+
*/
|
|
1320
|
+
async updateThread({
|
|
1321
|
+
id,
|
|
1322
|
+
title,
|
|
1323
|
+
metadata
|
|
1324
|
+
}) {
|
|
1325
|
+
const existingThread = await this.getThreadById({ threadId: id });
|
|
1326
|
+
if (!existingThread) {
|
|
1327
|
+
throw new error.MastraError({
|
|
1328
|
+
id: storage.createStorageErrorId("MSSQL", "UPDATE_THREAD", "NOT_FOUND"),
|
|
1329
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1330
|
+
category: error.ErrorCategory.USER,
|
|
1331
|
+
text: `Thread ${id} not found`,
|
|
1332
|
+
details: {
|
|
1333
|
+
threadId: id,
|
|
1334
|
+
title
|
|
1478
1335
|
}
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
const mergedMetadata = {
|
|
1339
|
+
...existingThread.metadata,
|
|
1340
|
+
...metadata
|
|
1341
|
+
};
|
|
1342
|
+
try {
|
|
1343
|
+
const table = getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.schema) });
|
|
1344
|
+
const sql5 = `UPDATE ${table}
|
|
1345
|
+
SET title = @title,
|
|
1346
|
+
metadata = @metadata,
|
|
1347
|
+
[updatedAt] = @updatedAt
|
|
1348
|
+
OUTPUT INSERTED.*
|
|
1349
|
+
WHERE id = @id`;
|
|
1350
|
+
const req = this.pool.request();
|
|
1351
|
+
req.input("id", id);
|
|
1352
|
+
req.input("title", title);
|
|
1353
|
+
req.input("metadata", JSON.stringify(mergedMetadata));
|
|
1354
|
+
req.input("updatedAt", /* @__PURE__ */ new Date());
|
|
1355
|
+
const result = await req.query(sql5);
|
|
1356
|
+
let thread = result.recordset && result.recordset[0];
|
|
1357
|
+
if (thread && "seq_id" in thread) {
|
|
1358
|
+
const { seq_id, ...rest } = thread;
|
|
1359
|
+
thread = rest;
|
|
1360
|
+
}
|
|
1361
|
+
if (!thread) {
|
|
1362
|
+
throw new error.MastraError({
|
|
1363
|
+
id: storage.createStorageErrorId("MSSQL", "UPDATE_THREAD", "NOT_FOUND"),
|
|
1364
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1365
|
+
category: error.ErrorCategory.USER,
|
|
1366
|
+
text: `Thread ${id} not found after update`,
|
|
1367
|
+
details: {
|
|
1368
|
+
threadId: id,
|
|
1369
|
+
title
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1479
1372
|
}
|
|
1373
|
+
return {
|
|
1374
|
+
...thread,
|
|
1375
|
+
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1376
|
+
createdAt: thread.createdAt,
|
|
1377
|
+
updatedAt: thread.updatedAt
|
|
1378
|
+
};
|
|
1480
1379
|
} catch (error$1) {
|
|
1481
1380
|
throw new error.MastraError(
|
|
1482
1381
|
{
|
|
1483
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
1382
|
+
id: storage.createStorageErrorId("MSSQL", "UPDATE_THREAD", "FAILED"),
|
|
1484
1383
|
domain: error.ErrorDomain.STORAGE,
|
|
1485
1384
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
1486
1385
|
details: {
|
|
1487
|
-
|
|
1386
|
+
threadId: id,
|
|
1387
|
+
title
|
|
1488
1388
|
}
|
|
1489
1389
|
},
|
|
1490
1390
|
error$1
|
|
1491
1391
|
);
|
|
1492
1392
|
}
|
|
1493
1393
|
}
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
case "boolean":
|
|
1501
|
-
return "DEFAULT 0";
|
|
1502
|
-
default:
|
|
1503
|
-
return super.getDefaultValue(type);
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
async createTable({
|
|
1507
|
-
tableName,
|
|
1508
|
-
schema
|
|
1509
|
-
}) {
|
|
1394
|
+
async deleteThread({ threadId }) {
|
|
1395
|
+
const messagesTable = getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) });
|
|
1396
|
+
const threadsTable = getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.schema) });
|
|
1397
|
+
const deleteMessagesSql = `DELETE FROM ${messagesTable} WHERE [thread_id] = @threadId`;
|
|
1398
|
+
const deleteThreadSql = `DELETE FROM ${threadsTable} WHERE id = @threadId`;
|
|
1399
|
+
const tx = this.pool.transaction();
|
|
1510
1400
|
try {
|
|
1511
|
-
|
|
1512
|
-
const
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
if (def.primaryKey) constraints.push("PRIMARY KEY");
|
|
1531
|
-
if (!def.nullable) constraints.push("NOT NULL");
|
|
1532
|
-
const isIndexed = !!def.primaryKey || uniqueConstraintColumns.includes(name);
|
|
1533
|
-
const useLargeStorage = largeDataColumns.includes(name);
|
|
1534
|
-
return `[${parsedName}] ${this.getSqlType(def.type, isIndexed, useLargeStorage)} ${constraints.join(" ")}`.trim();
|
|
1535
|
-
}).join(",\n");
|
|
1536
|
-
if (this.schemaName) {
|
|
1537
|
-
await this.setupSchema();
|
|
1538
|
-
}
|
|
1539
|
-
const checkTableRequest = this.pool.request();
|
|
1540
|
-
checkTableRequest.input(
|
|
1541
|
-
"tableName",
|
|
1542
|
-
getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) }).replace(/[[\]]/g, "").split(".").pop()
|
|
1401
|
+
await tx.begin();
|
|
1402
|
+
const req = tx.request();
|
|
1403
|
+
req.input("threadId", threadId);
|
|
1404
|
+
await req.query(deleteMessagesSql);
|
|
1405
|
+
await req.query(deleteThreadSql);
|
|
1406
|
+
await tx.commit();
|
|
1407
|
+
} catch (error$1) {
|
|
1408
|
+
await tx.rollback().catch(() => {
|
|
1409
|
+
});
|
|
1410
|
+
throw new error.MastraError(
|
|
1411
|
+
{
|
|
1412
|
+
id: storage.createStorageErrorId("MSSQL", "DELETE_THREAD", "FAILED"),
|
|
1413
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1414
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1415
|
+
details: {
|
|
1416
|
+
threadId
|
|
1417
|
+
}
|
|
1418
|
+
},
|
|
1419
|
+
error$1
|
|
1543
1420
|
);
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
const
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
async _getIncludedMessages({ include }) {
|
|
1424
|
+
if (!include || include.length === 0) return null;
|
|
1425
|
+
const unionQueries = [];
|
|
1426
|
+
const paramValues = [];
|
|
1427
|
+
let paramIdx = 1;
|
|
1428
|
+
const paramNames = [];
|
|
1429
|
+
const tableName = getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) });
|
|
1430
|
+
for (const inc of include) {
|
|
1431
|
+
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
1432
|
+
const pId = `@p${paramIdx}`;
|
|
1433
|
+
const pPrev = `@p${paramIdx + 1}`;
|
|
1434
|
+
const pNext = `@p${paramIdx + 2}`;
|
|
1435
|
+
unionQueries.push(
|
|
1436
|
+
`
|
|
1437
|
+
SELECT
|
|
1438
|
+
m.id,
|
|
1439
|
+
m.content,
|
|
1440
|
+
m.role,
|
|
1441
|
+
m.type,
|
|
1442
|
+
m.[createdAt],
|
|
1443
|
+
m.thread_id AS threadId,
|
|
1444
|
+
m.[resourceId],
|
|
1445
|
+
m.seq_id
|
|
1446
|
+
FROM (
|
|
1447
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
|
|
1448
|
+
FROM ${tableName}
|
|
1449
|
+
WHERE [thread_id] = (SELECT thread_id FROM ${tableName} WHERE id = ${pId})
|
|
1450
|
+
) AS m
|
|
1451
|
+
WHERE m.id = ${pId}
|
|
1452
|
+
OR EXISTS (
|
|
1453
|
+
SELECT 1
|
|
1454
|
+
FROM (
|
|
1455
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY [createdAt] ASC) as row_num
|
|
1456
|
+
FROM ${tableName}
|
|
1457
|
+
WHERE [thread_id] = (SELECT thread_id FROM ${tableName} WHERE id = ${pId})
|
|
1458
|
+
) AS target
|
|
1459
|
+
WHERE target.id = ${pId}
|
|
1460
|
+
AND (
|
|
1461
|
+
-- Get previous messages (messages that come BEFORE the target)
|
|
1462
|
+
(m.row_num < target.row_num AND m.row_num >= target.row_num - ${pPrev})
|
|
1463
|
+
OR
|
|
1464
|
+
-- Get next messages (messages that come AFTER the target)
|
|
1465
|
+
(m.row_num > target.row_num AND m.row_num <= target.row_num + ${pNext})
|
|
1466
|
+
)
|
|
1467
|
+
)
|
|
1468
|
+
`
|
|
1564
1469
|
);
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1470
|
+
paramValues.push(id, withPreviousMessages, withNextMessages);
|
|
1471
|
+
paramNames.push(`p${paramIdx}`, `p${paramIdx + 1}`, `p${paramIdx + 2}`);
|
|
1472
|
+
paramIdx += 3;
|
|
1473
|
+
}
|
|
1474
|
+
const finalQuery = `
|
|
1475
|
+
SELECT * FROM (
|
|
1476
|
+
${unionQueries.join(" UNION ALL ")}
|
|
1477
|
+
) AS union_result
|
|
1478
|
+
ORDER BY [seq_id] ASC
|
|
1479
|
+
`;
|
|
1480
|
+
const req = this.pool.request();
|
|
1481
|
+
for (let i = 0; i < paramValues.length; ++i) {
|
|
1482
|
+
req.input(paramNames[i], paramValues[i]);
|
|
1483
|
+
}
|
|
1484
|
+
const result = await req.query(finalQuery);
|
|
1485
|
+
const includedRows = result.recordset || [];
|
|
1486
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1487
|
+
const dedupedRows = includedRows.filter((row) => {
|
|
1488
|
+
if (seen.has(row.id)) return false;
|
|
1489
|
+
seen.add(row.id);
|
|
1490
|
+
return true;
|
|
1491
|
+
});
|
|
1492
|
+
return dedupedRows;
|
|
1493
|
+
}
|
|
1494
|
+
async listMessagesById({ messageIds }) {
|
|
1495
|
+
if (messageIds.length === 0) return { messages: [] };
|
|
1496
|
+
const selectStatement = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId`;
|
|
1497
|
+
const orderByStatement = `ORDER BY [seq_id] DESC`;
|
|
1498
|
+
try {
|
|
1499
|
+
let rows = [];
|
|
1500
|
+
let query = `${selectStatement} FROM ${getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) })} WHERE [id] IN (${messageIds.map((_, i) => `@id${i}`).join(", ")})`;
|
|
1501
|
+
const request = this.pool.request();
|
|
1502
|
+
messageIds.forEach((id, i) => request.input(`id${i}`, id));
|
|
1503
|
+
query += ` ${orderByStatement}`;
|
|
1504
|
+
const result = await request.query(query);
|
|
1505
|
+
const remainingRows = result.recordset || [];
|
|
1506
|
+
rows.push(...remainingRows);
|
|
1507
|
+
rows.sort((a, b) => {
|
|
1508
|
+
const timeDiff = a.seq_id - b.seq_id;
|
|
1509
|
+
return timeDiff;
|
|
1510
|
+
});
|
|
1511
|
+
const messagesWithParsedContent = rows.map((row) => {
|
|
1512
|
+
if (typeof row.content === "string") {
|
|
1513
|
+
try {
|
|
1514
|
+
return { ...row, content: JSON.parse(row.content) };
|
|
1515
|
+
} catch {
|
|
1516
|
+
return row;
|
|
1517
|
+
}
|
|
1581
1518
|
}
|
|
1582
|
-
|
|
1519
|
+
return row;
|
|
1520
|
+
});
|
|
1521
|
+
const cleanMessages = messagesWithParsedContent.map(({ seq_id, ...rest }) => rest);
|
|
1522
|
+
const list = new agent.MessageList().add(cleanMessages, "memory");
|
|
1523
|
+
return { messages: list.get.all.db() };
|
|
1583
1524
|
} catch (error$1) {
|
|
1584
|
-
|
|
1525
|
+
const mastraError = new error.MastraError(
|
|
1585
1526
|
{
|
|
1586
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
1527
|
+
id: storage.createStorageErrorId("MSSQL", "LIST_MESSAGES_BY_ID", "FAILED"),
|
|
1587
1528
|
domain: error.ErrorDomain.STORAGE,
|
|
1588
1529
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
1589
1530
|
details: {
|
|
1590
|
-
|
|
1531
|
+
messageIds: JSON.stringify(messageIds)
|
|
1591
1532
|
}
|
|
1592
1533
|
},
|
|
1593
1534
|
error$1
|
|
1594
1535
|
);
|
|
1536
|
+
this.logger?.error?.(mastraError.toString());
|
|
1537
|
+
this.logger?.trackException?.(mastraError);
|
|
1538
|
+
return { messages: [] };
|
|
1595
1539
|
}
|
|
1596
1540
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
* @param ifNotExists Array of column names to add if they don't exist
|
|
1602
|
-
*/
|
|
1603
|
-
async alterTable({
|
|
1604
|
-
tableName,
|
|
1605
|
-
schema,
|
|
1606
|
-
ifNotExists
|
|
1607
|
-
}) {
|
|
1608
|
-
const fullTableName = getTableName({ indexName: tableName, schemaName: getSchemaName(this.schemaName) });
|
|
1609
|
-
try {
|
|
1610
|
-
for (const columnName of ifNotExists) {
|
|
1611
|
-
if (schema[columnName]) {
|
|
1612
|
-
const columnCheckRequest = this.pool.request();
|
|
1613
|
-
columnCheckRequest.input("tableName", fullTableName.replace(/[[\]]/g, "").split(".").pop());
|
|
1614
|
-
columnCheckRequest.input("columnName", columnName);
|
|
1615
|
-
columnCheckRequest.input("schema", this.schemaName || "dbo");
|
|
1616
|
-
const checkSql = `SELECT 1 AS found FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @tableName AND COLUMN_NAME = @columnName`;
|
|
1617
|
-
const checkResult = await columnCheckRequest.query(checkSql);
|
|
1618
|
-
const columnExists = Array.isArray(checkResult.recordset) && checkResult.recordset.length > 0;
|
|
1619
|
-
if (!columnExists) {
|
|
1620
|
-
const columnDef = schema[columnName];
|
|
1621
|
-
const largeDataColumns = [
|
|
1622
|
-
"workingMemory",
|
|
1623
|
-
"snapshot",
|
|
1624
|
-
"metadata",
|
|
1625
|
-
"content",
|
|
1626
|
-
"input",
|
|
1627
|
-
"output",
|
|
1628
|
-
"instructions",
|
|
1629
|
-
"other"
|
|
1630
|
-
];
|
|
1631
|
-
const useLargeStorage = largeDataColumns.includes(columnName);
|
|
1632
|
-
const isIndexed = !!columnDef.primaryKey;
|
|
1633
|
-
const sqlType = this.getSqlType(columnDef.type, isIndexed, useLargeStorage);
|
|
1634
|
-
const nullable = columnDef.nullable === false ? "NOT NULL" : "";
|
|
1635
|
-
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
1636
|
-
const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
|
|
1637
|
-
const alterSql = `ALTER TABLE ${fullTableName} ADD [${parsedColumnName}] ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
1638
|
-
await this.pool.request().query(alterSql);
|
|
1639
|
-
this.logger?.debug?.(`Ensured column ${parsedColumnName} exists in table ${fullTableName}`);
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
} catch (error$1) {
|
|
1541
|
+
async listMessages(args) {
|
|
1542
|
+
const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
|
|
1543
|
+
const threadIds = Array.isArray(threadId) ? threadId : [threadId];
|
|
1544
|
+
if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
|
|
1644
1545
|
throw new error.MastraError(
|
|
1645
1546
|
{
|
|
1646
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
1547
|
+
id: storage.createStorageErrorId("MSSQL", "LIST_MESSAGES", "INVALID_THREAD_ID"),
|
|
1647
1548
|
domain: error.ErrorDomain.STORAGE,
|
|
1648
1549
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
1649
|
-
details: {
|
|
1650
|
-
tableName
|
|
1651
|
-
}
|
|
1550
|
+
details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
|
|
1652
1551
|
},
|
|
1653
|
-
|
|
1552
|
+
new Error("threadId must be a non-empty string or array of non-empty strings")
|
|
1654
1553
|
);
|
|
1655
1554
|
}
|
|
1656
|
-
|
|
1657
|
-
|
|
1555
|
+
if (page < 0) {
|
|
1556
|
+
throw new error.MastraError({
|
|
1557
|
+
id: storage.createStorageErrorId("MSSQL", "LIST_MESSAGES", "INVALID_PAGE"),
|
|
1558
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1559
|
+
category: error.ErrorCategory.USER,
|
|
1560
|
+
text: "Page number must be non-negative",
|
|
1561
|
+
details: {
|
|
1562
|
+
threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
|
|
1563
|
+
page
|
|
1564
|
+
}
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
const perPage = storage.normalizePerPage(perPageInput, 40);
|
|
1568
|
+
const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
|
|
1658
1569
|
try {
|
|
1659
|
-
const
|
|
1660
|
-
const
|
|
1661
|
-
const
|
|
1662
|
-
const
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1570
|
+
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
1571
|
+
const orderByStatement = `ORDER BY [${field}] ${direction}, [seq_id] ${direction}`;
|
|
1572
|
+
const tableName = getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) });
|
|
1573
|
+
const baseQuery = `SELECT seq_id, id, content, role, type, [createdAt], thread_id AS threadId, resourceId FROM ${tableName}`;
|
|
1574
|
+
const filters = {
|
|
1575
|
+
thread_id: threadIds.length === 1 ? threadIds[0] : { $in: threadIds },
|
|
1576
|
+
...resourceId ? { resourceId } : {},
|
|
1577
|
+
...buildDateRangeFilter(filter?.dateRange, "createdAt")
|
|
1578
|
+
};
|
|
1579
|
+
const { sql: actualWhereClause = "", params: whereParams } = prepareWhereClause(
|
|
1580
|
+
filters);
|
|
1581
|
+
const bindWhereParams = (req) => {
|
|
1582
|
+
Object.entries(whereParams).forEach(([paramName, paramValue]) => req.input(paramName, paramValue));
|
|
1583
|
+
};
|
|
1584
|
+
const countRequest = this.pool.request();
|
|
1585
|
+
bindWhereParams(countRequest);
|
|
1586
|
+
const countResult = await countRequest.query(`SELECT COUNT(*) as total FROM ${tableName}${actualWhereClause}`);
|
|
1587
|
+
const total = parseInt(countResult.recordset[0]?.total, 10) || 0;
|
|
1588
|
+
const fetchBaseMessages = async () => {
|
|
1589
|
+
const request = this.pool.request();
|
|
1590
|
+
bindWhereParams(request);
|
|
1591
|
+
if (perPageInput === false) {
|
|
1592
|
+
const result2 = await request.query(`${baseQuery}${actualWhereClause} ${orderByStatement}`);
|
|
1593
|
+
return result2.recordset || [];
|
|
1669
1594
|
}
|
|
1595
|
+
request.input("offset", offset);
|
|
1596
|
+
request.input("limit", perPage > 2147483647 ? sql__default.default.BigInt : sql__default.default.Int, perPage);
|
|
1597
|
+
const result = await request.query(
|
|
1598
|
+
`${baseQuery}${actualWhereClause} ${orderByStatement} OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
|
|
1599
|
+
);
|
|
1600
|
+
return result.recordset || [];
|
|
1601
|
+
};
|
|
1602
|
+
const baseRows = perPage === 0 ? [] : await fetchBaseMessages();
|
|
1603
|
+
const messages = [...baseRows];
|
|
1604
|
+
const seqById = /* @__PURE__ */ new Map();
|
|
1605
|
+
messages.forEach((msg) => {
|
|
1606
|
+
if (typeof msg.seq_id === "number") seqById.set(msg.id, msg.seq_id);
|
|
1670
1607
|
});
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1608
|
+
if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
|
|
1609
|
+
return {
|
|
1610
|
+
messages: [],
|
|
1611
|
+
total: 0,
|
|
1612
|
+
page,
|
|
1613
|
+
perPage: perPageForResponse,
|
|
1614
|
+
hasMore: false
|
|
1615
|
+
};
|
|
1675
1616
|
}
|
|
1676
|
-
if (
|
|
1677
|
-
const
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1617
|
+
if (include?.length) {
|
|
1618
|
+
const messageIds = new Set(messages.map((m) => m.id));
|
|
1619
|
+
const includeMessages = await this._getIncludedMessages({ include });
|
|
1620
|
+
includeMessages?.forEach((msg) => {
|
|
1621
|
+
if (!messageIds.has(msg.id)) {
|
|
1622
|
+
messages.push(msg);
|
|
1623
|
+
messageIds.add(msg.id);
|
|
1624
|
+
if (typeof msg.seq_id === "number") seqById.set(msg.id, msg.seq_id);
|
|
1625
|
+
}
|
|
1626
|
+
});
|
|
1682
1627
|
}
|
|
1683
|
-
|
|
1628
|
+
const parsed = this._parseAndFormatMessages(messages, "v2");
|
|
1629
|
+
const mult = direction === "ASC" ? 1 : -1;
|
|
1630
|
+
const finalMessages = parsed.sort((a, b) => {
|
|
1631
|
+
const aVal = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
|
|
1632
|
+
const bVal = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
|
|
1633
|
+
if (aVal == null || bVal == null) {
|
|
1634
|
+
return aVal == null && bVal == null ? a.id.localeCompare(b.id) : aVal == null ? 1 : -1;
|
|
1635
|
+
}
|
|
1636
|
+
const diff = (typeof aVal === "number" && typeof bVal === "number" ? aVal - bVal : String(aVal).localeCompare(String(bVal))) * mult;
|
|
1637
|
+
if (diff !== 0) return diff;
|
|
1638
|
+
const seqA = seqById.get(a.id);
|
|
1639
|
+
const seqB = seqById.get(b.id);
|
|
1640
|
+
return seqA != null && seqB != null ? (seqA - seqB) * mult : a.id.localeCompare(b.id);
|
|
1641
|
+
});
|
|
1642
|
+
const threadIdSet = new Set(threadIds);
|
|
1643
|
+
const returnedThreadMessageCount = finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).length;
|
|
1644
|
+
const hasMore = perPageInput !== false && returnedThreadMessageCount < total && offset + perPage < total;
|
|
1645
|
+
return {
|
|
1646
|
+
messages: finalMessages,
|
|
1647
|
+
total,
|
|
1648
|
+
page,
|
|
1649
|
+
perPage: perPageForResponse,
|
|
1650
|
+
hasMore
|
|
1651
|
+
};
|
|
1684
1652
|
} catch (error$1) {
|
|
1685
|
-
|
|
1653
|
+
const mastraError = new error.MastraError(
|
|
1686
1654
|
{
|
|
1687
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
1655
|
+
id: storage.createStorageErrorId("MSSQL", "LIST_MESSAGES", "FAILED"),
|
|
1688
1656
|
domain: error.ErrorDomain.STORAGE,
|
|
1689
1657
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
1690
1658
|
details: {
|
|
1691
|
-
|
|
1659
|
+
threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
|
|
1660
|
+
resourceId: resourceId ?? ""
|
|
1692
1661
|
}
|
|
1693
1662
|
},
|
|
1694
1663
|
error$1
|
|
1695
1664
|
);
|
|
1665
|
+
this.logger?.error?.(mastraError.toString());
|
|
1666
|
+
this.logger?.trackException?.(mastraError);
|
|
1667
|
+
return {
|
|
1668
|
+
messages: [],
|
|
1669
|
+
total: 0,
|
|
1670
|
+
page,
|
|
1671
|
+
perPage: perPageForResponse,
|
|
1672
|
+
hasMore: false
|
|
1673
|
+
};
|
|
1696
1674
|
}
|
|
1697
1675
|
}
|
|
1698
|
-
async
|
|
1699
|
-
|
|
1676
|
+
async saveMessages({ messages }) {
|
|
1677
|
+
if (messages.length === 0) return { messages: [] };
|
|
1678
|
+
const threadId = messages[0]?.threadId;
|
|
1679
|
+
if (!threadId) {
|
|
1680
|
+
throw new error.MastraError({
|
|
1681
|
+
id: storage.createStorageErrorId("MSSQL", "SAVE_MESSAGES", "INVALID_THREAD_ID"),
|
|
1682
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1683
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1684
|
+
text: `Thread ID is required`
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
const thread = await this.getThreadById({ threadId });
|
|
1688
|
+
if (!thread) {
|
|
1689
|
+
throw new error.MastraError({
|
|
1690
|
+
id: storage.createStorageErrorId("MSSQL", "SAVE_MESSAGES", "THREAD_NOT_FOUND"),
|
|
1691
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1692
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1693
|
+
text: `Thread ${threadId} not found`,
|
|
1694
|
+
details: { threadId }
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
const tableMessages = getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) });
|
|
1698
|
+
const tableThreads = getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.schema) });
|
|
1700
1699
|
try {
|
|
1700
|
+
const transaction = this.pool.transaction();
|
|
1701
1701
|
await transaction.begin();
|
|
1702
|
-
|
|
1703
|
-
|
|
1702
|
+
try {
|
|
1703
|
+
for (const message of messages) {
|
|
1704
|
+
if (!message.threadId) {
|
|
1705
|
+
throw new Error(
|
|
1706
|
+
`Expected to find a threadId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1707
|
+
);
|
|
1708
|
+
}
|
|
1709
|
+
if (!message.resourceId) {
|
|
1710
|
+
throw new Error(
|
|
1711
|
+
`Expected to find a resourceId for message, but couldn't find one. An unexpected error has occurred.`
|
|
1712
|
+
);
|
|
1713
|
+
}
|
|
1714
|
+
const request = transaction.request();
|
|
1715
|
+
request.input("id", message.id);
|
|
1716
|
+
request.input("thread_id", message.threadId);
|
|
1717
|
+
request.input(
|
|
1718
|
+
"content",
|
|
1719
|
+
typeof message.content === "string" ? message.content : JSON.stringify(message.content)
|
|
1720
|
+
);
|
|
1721
|
+
request.input("createdAt", sql__default.default.DateTime2, message.createdAt);
|
|
1722
|
+
request.input("role", message.role);
|
|
1723
|
+
request.input("type", message.type || "v2");
|
|
1724
|
+
request.input("resourceId", message.resourceId);
|
|
1725
|
+
const mergeSql = `MERGE INTO ${tableMessages} AS target
|
|
1726
|
+
USING (SELECT @id AS id) AS src
|
|
1727
|
+
ON target.id = src.id
|
|
1728
|
+
WHEN MATCHED THEN UPDATE SET
|
|
1729
|
+
thread_id = @thread_id,
|
|
1730
|
+
content = @content,
|
|
1731
|
+
[createdAt] = @createdAt,
|
|
1732
|
+
role = @role,
|
|
1733
|
+
type = @type,
|
|
1734
|
+
resourceId = @resourceId
|
|
1735
|
+
WHEN NOT MATCHED THEN INSERT (id, thread_id, content, [createdAt], role, type, resourceId)
|
|
1736
|
+
VALUES (@id, @thread_id, @content, @createdAt, @role, @type, @resourceId);`;
|
|
1737
|
+
await request.query(mergeSql);
|
|
1738
|
+
}
|
|
1739
|
+
const threadReq = transaction.request();
|
|
1740
|
+
threadReq.input("updatedAt", sql__default.default.DateTime2, /* @__PURE__ */ new Date());
|
|
1741
|
+
threadReq.input("id", threadId);
|
|
1742
|
+
await threadReq.query(`UPDATE ${tableThreads} SET [updatedAt] = @updatedAt WHERE id = @id`);
|
|
1743
|
+
await transaction.commit();
|
|
1744
|
+
} catch (error) {
|
|
1745
|
+
await transaction.rollback();
|
|
1746
|
+
throw error;
|
|
1704
1747
|
}
|
|
1705
|
-
|
|
1748
|
+
const messagesWithParsedContent = messages.map((message) => {
|
|
1749
|
+
if (typeof message.content === "string") {
|
|
1750
|
+
try {
|
|
1751
|
+
return { ...message, content: JSON.parse(message.content) };
|
|
1752
|
+
} catch {
|
|
1753
|
+
return message;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
return message;
|
|
1757
|
+
});
|
|
1758
|
+
const list = new agent.MessageList().add(messagesWithParsedContent, "memory");
|
|
1759
|
+
return { messages: list.get.all.db() };
|
|
1706
1760
|
} catch (error$1) {
|
|
1707
|
-
await transaction.rollback();
|
|
1708
1761
|
throw new error.MastraError(
|
|
1709
1762
|
{
|
|
1710
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
1763
|
+
id: storage.createStorageErrorId("MSSQL", "SAVE_MESSAGES", "FAILED"),
|
|
1711
1764
|
domain: error.ErrorDomain.STORAGE,
|
|
1712
1765
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
1713
|
-
details: {
|
|
1714
|
-
tableName,
|
|
1715
|
-
numberOfRecords: records.length
|
|
1716
|
-
}
|
|
1766
|
+
details: { threadId }
|
|
1717
1767
|
},
|
|
1718
1768
|
error$1
|
|
1719
1769
|
);
|
|
1720
1770
|
}
|
|
1721
1771
|
}
|
|
1722
|
-
async
|
|
1772
|
+
async updateMessages({
|
|
1773
|
+
messages
|
|
1774
|
+
}) {
|
|
1775
|
+
if (!messages || messages.length === 0) {
|
|
1776
|
+
return [];
|
|
1777
|
+
}
|
|
1778
|
+
const messageIds = messages.map((m) => m.id);
|
|
1779
|
+
const idParams = messageIds.map((_, i) => `@id${i}`).join(", ");
|
|
1780
|
+
let selectQuery = `SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId FROM ${getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) })}`;
|
|
1781
|
+
if (idParams.length > 0) {
|
|
1782
|
+
selectQuery += ` WHERE id IN (${idParams})`;
|
|
1783
|
+
} else {
|
|
1784
|
+
return [];
|
|
1785
|
+
}
|
|
1786
|
+
const selectReq = this.pool.request();
|
|
1787
|
+
messageIds.forEach((id, i) => selectReq.input(`id${i}`, id));
|
|
1788
|
+
const existingMessagesDb = (await selectReq.query(selectQuery)).recordset;
|
|
1789
|
+
if (!existingMessagesDb || existingMessagesDb.length === 0) {
|
|
1790
|
+
return [];
|
|
1791
|
+
}
|
|
1792
|
+
const existingMessages = existingMessagesDb.map((msg) => {
|
|
1793
|
+
if (typeof msg.content === "string") {
|
|
1794
|
+
try {
|
|
1795
|
+
msg.content = JSON.parse(msg.content);
|
|
1796
|
+
} catch {
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
return msg;
|
|
1800
|
+
});
|
|
1801
|
+
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
1802
|
+
const transaction = this.pool.transaction();
|
|
1723
1803
|
try {
|
|
1724
|
-
|
|
1725
|
-
|
|
1804
|
+
await transaction.begin();
|
|
1805
|
+
for (const existingMessage of existingMessages) {
|
|
1806
|
+
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
1807
|
+
if (!updatePayload) continue;
|
|
1808
|
+
const { id, ...fieldsToUpdate } = updatePayload;
|
|
1809
|
+
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
1810
|
+
threadIdsToUpdate.add(existingMessage.threadId);
|
|
1811
|
+
if (updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
|
|
1812
|
+
threadIdsToUpdate.add(updatePayload.threadId);
|
|
1813
|
+
}
|
|
1814
|
+
const setClauses = [];
|
|
1815
|
+
const req = transaction.request();
|
|
1816
|
+
req.input("id", id);
|
|
1817
|
+
const columnMapping = { threadId: "thread_id" };
|
|
1818
|
+
const updatableFields = { ...fieldsToUpdate };
|
|
1819
|
+
if (updatableFields.content) {
|
|
1820
|
+
const newContent = {
|
|
1821
|
+
...existingMessage.content,
|
|
1822
|
+
...updatableFields.content,
|
|
1823
|
+
...existingMessage.content?.metadata && updatableFields.content.metadata ? { metadata: { ...existingMessage.content.metadata, ...updatableFields.content.metadata } } : {}
|
|
1824
|
+
};
|
|
1825
|
+
setClauses.push(`content = @content`);
|
|
1826
|
+
req.input("content", JSON.stringify(newContent));
|
|
1827
|
+
delete updatableFields.content;
|
|
1828
|
+
}
|
|
1829
|
+
for (const key in updatableFields) {
|
|
1830
|
+
if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
|
|
1831
|
+
const dbColumn = columnMapping[key] || key;
|
|
1832
|
+
setClauses.push(`[${dbColumn}] = @${dbColumn}`);
|
|
1833
|
+
req.input(dbColumn, updatableFields[key]);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
if (setClauses.length > 0) {
|
|
1837
|
+
const updateSql = `UPDATE ${getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) })} SET ${setClauses.join(", ")} WHERE id = @id`;
|
|
1838
|
+
await req.query(updateSql);
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
if (threadIdsToUpdate.size > 0) {
|
|
1842
|
+
const threadIdParams = Array.from(threadIdsToUpdate).map((_, i) => `@tid${i}`).join(", ");
|
|
1843
|
+
const threadReq = transaction.request();
|
|
1844
|
+
Array.from(threadIdsToUpdate).forEach((tid, i) => threadReq.input(`tid${i}`, tid));
|
|
1845
|
+
threadReq.input("updatedAt", (/* @__PURE__ */ new Date()).toISOString());
|
|
1846
|
+
const threadSql = `UPDATE ${getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.schema) })} SET updatedAt = @updatedAt WHERE id IN (${threadIdParams})`;
|
|
1847
|
+
await threadReq.query(threadSql);
|
|
1848
|
+
}
|
|
1849
|
+
await transaction.commit();
|
|
1726
1850
|
} catch (error$1) {
|
|
1851
|
+
await transaction.rollback();
|
|
1727
1852
|
throw new error.MastraError(
|
|
1728
1853
|
{
|
|
1729
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
1854
|
+
id: storage.createStorageErrorId("MSSQL", "UPDATE_MESSAGES", "FAILED"),
|
|
1730
1855
|
domain: error.ErrorDomain.STORAGE,
|
|
1731
|
-
category: error.ErrorCategory.THIRD_PARTY
|
|
1732
|
-
details: {
|
|
1733
|
-
tableName
|
|
1734
|
-
}
|
|
1856
|
+
category: error.ErrorCategory.THIRD_PARTY
|
|
1735
1857
|
},
|
|
1736
1858
|
error$1
|
|
1737
1859
|
);
|
|
1738
1860
|
}
|
|
1861
|
+
const refetchReq = this.pool.request();
|
|
1862
|
+
messageIds.forEach((id, i) => refetchReq.input(`id${i}`, id));
|
|
1863
|
+
const updatedMessages = (await refetchReq.query(selectQuery)).recordset;
|
|
1864
|
+
return (updatedMessages || []).map((message) => {
|
|
1865
|
+
if (typeof message.content === "string") {
|
|
1866
|
+
try {
|
|
1867
|
+
message.content = JSON.parse(message.content);
|
|
1868
|
+
} catch {
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
return message;
|
|
1872
|
+
});
|
|
1739
1873
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
prepareValue(value, columnName, tableName) {
|
|
1744
|
-
if (value === null || value === void 0) {
|
|
1745
|
-
return value;
|
|
1746
|
-
}
|
|
1747
|
-
if (value instanceof Date) {
|
|
1748
|
-
return value;
|
|
1749
|
-
}
|
|
1750
|
-
const schema = storage.TABLE_SCHEMAS[tableName];
|
|
1751
|
-
const columnSchema = schema?.[columnName];
|
|
1752
|
-
if (columnSchema?.type === "boolean") {
|
|
1753
|
-
return value ? 1 : 0;
|
|
1874
|
+
async deleteMessages(messageIds) {
|
|
1875
|
+
if (!messageIds || messageIds.length === 0) {
|
|
1876
|
+
return;
|
|
1754
1877
|
}
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1878
|
+
try {
|
|
1879
|
+
const messageTableName = getTableName2({ indexName: storage.TABLE_MESSAGES, schemaName: getSchemaName2(this.schema) });
|
|
1880
|
+
const threadTableName = getTableName2({ indexName: storage.TABLE_THREADS, schemaName: getSchemaName2(this.schema) });
|
|
1881
|
+
const placeholders = messageIds.map((_, idx) => `@p${idx + 1}`).join(",");
|
|
1882
|
+
const request = this.pool.request();
|
|
1883
|
+
messageIds.forEach((id, idx) => {
|
|
1884
|
+
request.input(`p${idx + 1}`, id);
|
|
1885
|
+
});
|
|
1886
|
+
const messages = await request.query(
|
|
1887
|
+
`SELECT DISTINCT [thread_id] FROM ${messageTableName} WHERE [id] IN (${placeholders})`
|
|
1888
|
+
);
|
|
1889
|
+
const threadIds = messages.recordset?.map((msg) => msg.thread_id).filter(Boolean) || [];
|
|
1890
|
+
const transaction = this.pool.transaction();
|
|
1891
|
+
await transaction.begin();
|
|
1892
|
+
try {
|
|
1893
|
+
const deleteRequest = transaction.request();
|
|
1894
|
+
messageIds.forEach((id, idx) => {
|
|
1895
|
+
deleteRequest.input(`p${idx + 1}`, id);
|
|
1896
|
+
});
|
|
1897
|
+
await deleteRequest.query(`DELETE FROM ${messageTableName} WHERE [id] IN (${placeholders})`);
|
|
1898
|
+
if (threadIds.length > 0) {
|
|
1899
|
+
for (const threadId of threadIds) {
|
|
1900
|
+
const updateRequest = transaction.request();
|
|
1901
|
+
updateRequest.input("p1", threadId);
|
|
1902
|
+
await updateRequest.query(`UPDATE ${threadTableName} SET [updatedAt] = GETDATE() WHERE [id] = @p1`);
|
|
1763
1903
|
}
|
|
1764
1904
|
}
|
|
1765
|
-
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
|
-
|
|
1905
|
+
await transaction.commit();
|
|
1906
|
+
} catch (error) {
|
|
1907
|
+
try {
|
|
1908
|
+
await transaction.rollback();
|
|
1909
|
+
} catch {
|
|
1910
|
+
}
|
|
1911
|
+
throw error;
|
|
1769
1912
|
}
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
getMssqlType(tableName, columnName) {
|
|
1781
|
-
const col = storage.TABLE_SCHEMAS[tableName]?.[columnName];
|
|
1782
|
-
switch (col?.type) {
|
|
1783
|
-
case "text":
|
|
1784
|
-
return sql3__default.default.NVarChar;
|
|
1785
|
-
case "timestamp":
|
|
1786
|
-
return sql3__default.default.DateTime2;
|
|
1787
|
-
case "uuid":
|
|
1788
|
-
return sql3__default.default.UniqueIdentifier;
|
|
1789
|
-
case "jsonb":
|
|
1790
|
-
return sql3__default.default.NVarChar;
|
|
1791
|
-
case "integer":
|
|
1792
|
-
return sql3__default.default.Int;
|
|
1793
|
-
case "bigint":
|
|
1794
|
-
return sql3__default.default.BigInt;
|
|
1795
|
-
case "float":
|
|
1796
|
-
return sql3__default.default.Float;
|
|
1797
|
-
case "boolean":
|
|
1798
|
-
return sql3__default.default.Bit;
|
|
1799
|
-
default:
|
|
1800
|
-
return sql3__default.default.NVarChar;
|
|
1913
|
+
} catch (error$1) {
|
|
1914
|
+
throw new error.MastraError(
|
|
1915
|
+
{
|
|
1916
|
+
id: storage.createStorageErrorId("MSSQL", "DELETE_MESSAGES", "FAILED"),
|
|
1917
|
+
domain: error.ErrorDomain.STORAGE,
|
|
1918
|
+
category: error.ErrorCategory.THIRD_PARTY,
|
|
1919
|
+
details: { messageIds: messageIds.join(", ") }
|
|
1920
|
+
},
|
|
1921
|
+
error$1
|
|
1922
|
+
);
|
|
1801
1923
|
}
|
|
1802
1924
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
*/
|
|
1806
|
-
async update({
|
|
1807
|
-
tableName,
|
|
1808
|
-
keys,
|
|
1809
|
-
data,
|
|
1810
|
-
transaction
|
|
1811
|
-
}) {
|
|
1925
|
+
async getResourceById({ resourceId }) {
|
|
1926
|
+
const tableName = getTableName2({ indexName: storage.TABLE_RESOURCES, schemaName: getSchemaName2(this.schema) });
|
|
1812
1927
|
try {
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
text: "Cannot update with empty data payload"
|
|
1819
|
-
});
|
|
1820
|
-
}
|
|
1821
|
-
if (!keys || Object.keys(keys).length === 0) {
|
|
1822
|
-
throw new error.MastraError({
|
|
1823
|
-
id: storage.createStorageErrorId("MSSQL", "UPDATE", "EMPTY_KEYS"),
|
|
1824
|
-
domain: error.ErrorDomain.STORAGE,
|
|
1825
|
-
category: error.ErrorCategory.USER,
|
|
1826
|
-
text: "Cannot update without keys to identify records"
|
|
1827
|
-
});
|
|
1928
|
+
const req = this.pool.request();
|
|
1929
|
+
req.input("resourceId", resourceId);
|
|
1930
|
+
const result = (await req.query(`SELECT * FROM ${tableName} WHERE id = @resourceId`)).recordset[0];
|
|
1931
|
+
if (!result) {
|
|
1932
|
+
return null;
|
|
1828
1933
|
}
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1837
|
-
if (preparedValue === null || preparedValue === void 0) {
|
|
1838
|
-
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
1839
|
-
} else {
|
|
1840
|
-
request.input(paramName, preparedValue);
|
|
1841
|
-
}
|
|
1842
|
-
});
|
|
1843
|
-
const whereConditions = [];
|
|
1844
|
-
Object.entries(keys).forEach(([key, value]) => {
|
|
1845
|
-
const parsedKey = utils.parseSqlIdentifier(key, "column name");
|
|
1846
|
-
const paramName = `where${paramIndex++}`;
|
|
1847
|
-
whereConditions.push(`[${parsedKey}] = @${paramName}`);
|
|
1848
|
-
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1849
|
-
if (preparedValue === null || preparedValue === void 0) {
|
|
1850
|
-
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
1851
|
-
} else {
|
|
1852
|
-
request.input(paramName, preparedValue);
|
|
1853
|
-
}
|
|
1854
|
-
});
|
|
1855
|
-
const tableName_ = getTableName({
|
|
1856
|
-
indexName: tableName,
|
|
1857
|
-
schemaName: getSchemaName(this.schemaName)
|
|
1858
|
-
});
|
|
1859
|
-
const updateSql = `UPDATE ${tableName_} SET ${setClauses.join(", ")} WHERE ${whereConditions.join(" AND ")}`;
|
|
1860
|
-
await request.query(updateSql);
|
|
1934
|
+
return {
|
|
1935
|
+
id: result.id,
|
|
1936
|
+
createdAt: result.createdAt,
|
|
1937
|
+
updatedAt: result.updatedAt,
|
|
1938
|
+
workingMemory: result.workingMemory,
|
|
1939
|
+
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
|
|
1940
|
+
};
|
|
1861
1941
|
} catch (error$1) {
|
|
1862
|
-
|
|
1942
|
+
const mastraError = new error.MastraError(
|
|
1863
1943
|
{
|
|
1864
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
1944
|
+
id: storage.createStorageErrorId("MSSQL", "GET_RESOURCE_BY_ID", "FAILED"),
|
|
1865
1945
|
domain: error.ErrorDomain.STORAGE,
|
|
1866
1946
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
1867
|
-
details: {
|
|
1868
|
-
tableName
|
|
1869
|
-
}
|
|
1947
|
+
details: { resourceId }
|
|
1870
1948
|
},
|
|
1871
1949
|
error$1
|
|
1872
1950
|
);
|
|
1951
|
+
this.logger?.error?.(mastraError.toString());
|
|
1952
|
+
this.logger?.trackException?.(mastraError);
|
|
1953
|
+
throw mastraError;
|
|
1873
1954
|
}
|
|
1874
1955
|
}
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1956
|
+
async saveResource({ resource }) {
|
|
1957
|
+
await this.db.insert({
|
|
1958
|
+
tableName: storage.TABLE_RESOURCES,
|
|
1959
|
+
record: {
|
|
1960
|
+
...resource,
|
|
1961
|
+
metadata: resource.metadata
|
|
1962
|
+
}
|
|
1963
|
+
});
|
|
1964
|
+
return resource;
|
|
1965
|
+
}
|
|
1966
|
+
async updateResource({
|
|
1967
|
+
resourceId,
|
|
1968
|
+
workingMemory,
|
|
1969
|
+
metadata
|
|
1881
1970
|
}) {
|
|
1882
|
-
const transaction = this.pool.transaction();
|
|
1883
1971
|
try {
|
|
1884
|
-
await
|
|
1885
|
-
|
|
1886
|
-
|
|
1972
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
1973
|
+
if (!existingResource) {
|
|
1974
|
+
const newResource = {
|
|
1975
|
+
id: resourceId,
|
|
1976
|
+
workingMemory,
|
|
1977
|
+
metadata: metadata || {},
|
|
1978
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1979
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1980
|
+
};
|
|
1981
|
+
return this.saveResource({ resource: newResource });
|
|
1887
1982
|
}
|
|
1888
|
-
|
|
1983
|
+
const updatedResource = {
|
|
1984
|
+
...existingResource,
|
|
1985
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
1986
|
+
metadata: {
|
|
1987
|
+
...existingResource.metadata,
|
|
1988
|
+
...metadata
|
|
1989
|
+
},
|
|
1990
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1991
|
+
};
|
|
1992
|
+
const tableName = getTableName2({ indexName: storage.TABLE_RESOURCES, schemaName: getSchemaName2(this.schema) });
|
|
1993
|
+
const updates = [];
|
|
1994
|
+
const req = this.pool.request();
|
|
1995
|
+
if (workingMemory !== void 0) {
|
|
1996
|
+
updates.push("workingMemory = @workingMemory");
|
|
1997
|
+
req.input("workingMemory", workingMemory);
|
|
1998
|
+
}
|
|
1999
|
+
if (metadata) {
|
|
2000
|
+
updates.push("metadata = @metadata");
|
|
2001
|
+
req.input("metadata", JSON.stringify(updatedResource.metadata));
|
|
2002
|
+
}
|
|
2003
|
+
updates.push("updatedAt = @updatedAt");
|
|
2004
|
+
req.input("updatedAt", updatedResource.updatedAt.toISOString());
|
|
2005
|
+
req.input("id", resourceId);
|
|
2006
|
+
await req.query(`UPDATE ${tableName} SET ${updates.join(", ")} WHERE id = @id`);
|
|
2007
|
+
return updatedResource;
|
|
1889
2008
|
} catch (error$1) {
|
|
1890
|
-
|
|
1891
|
-
throw new error.MastraError(
|
|
2009
|
+
const mastraError = new error.MastraError(
|
|
1892
2010
|
{
|
|
1893
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
2011
|
+
id: storage.createStorageErrorId("MSSQL", "UPDATE_RESOURCE", "FAILED"),
|
|
1894
2012
|
domain: error.ErrorDomain.STORAGE,
|
|
1895
2013
|
category: error.ErrorCategory.THIRD_PARTY,
|
|
1896
|
-
details: {
|
|
1897
|
-
tableName,
|
|
1898
|
-
numberOfRecords: updates.length
|
|
1899
|
-
}
|
|
2014
|
+
details: { resourceId }
|
|
1900
2015
|
},
|
|
1901
2016
|
error$1
|
|
1902
2017
|
);
|
|
2018
|
+
this.logger?.error?.(mastraError.toString());
|
|
2019
|
+
this.logger?.trackException?.(mastraError);
|
|
2020
|
+
throw mastraError;
|
|
1903
2021
|
}
|
|
1904
2022
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
2023
|
+
};
|
|
2024
|
+
var ObservabilityMSSQL = class extends storage.ObservabilityStorage {
|
|
2025
|
+
pool;
|
|
2026
|
+
db;
|
|
2027
|
+
schema;
|
|
2028
|
+
needsConnect;
|
|
2029
|
+
constructor(config) {
|
|
2030
|
+
super();
|
|
2031
|
+
const { pool, db, schema, needsConnect } = resolveMssqlConfig(config);
|
|
2032
|
+
this.pool = pool;
|
|
2033
|
+
this.db = db;
|
|
2034
|
+
this.schema = schema;
|
|
2035
|
+
this.needsConnect = needsConnect;
|
|
2036
|
+
}
|
|
2037
|
+
async init() {
|
|
2038
|
+
if (this.needsConnect) {
|
|
2039
|
+
await this.pool.connect();
|
|
2040
|
+
this.needsConnect = false;
|
|
1911
2041
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
});
|
|
1916
|
-
|
|
2042
|
+
await this.db.createTable({ tableName: storage.TABLE_SPANS, schema: storage.SPAN_SCHEMA });
|
|
2043
|
+
}
|
|
2044
|
+
async dangerouslyClearAll() {
|
|
2045
|
+
await this.db.clearTable({ tableName: storage.TABLE_SPANS });
|
|
2046
|
+
}
|
|
2047
|
+
get tracingStrategy() {
|
|
2048
|
+
return {
|
|
2049
|
+
preferred: "batch-with-updates",
|
|
2050
|
+
supported: ["batch-with-updates", "insert-only"]
|
|
2051
|
+
};
|
|
2052
|
+
}
|
|
2053
|
+
async createSpan(span) {
|
|
1917
2054
|
try {
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
const preparedValue = this.prepareValue(value, key, tableName);
|
|
1928
|
-
if (preparedValue === null || preparedValue === void 0) {
|
|
1929
|
-
request.input(paramName, this.getMssqlType(tableName, key), null);
|
|
1930
|
-
} else {
|
|
1931
|
-
request.input(paramName, preparedValue);
|
|
1932
|
-
}
|
|
1933
|
-
});
|
|
1934
|
-
const deleteSql = `DELETE FROM ${tableName_} WHERE ${conditions.join(" AND ")}`;
|
|
1935
|
-
await request.query(deleteSql);
|
|
1936
|
-
}
|
|
1937
|
-
await transaction.commit();
|
|
2055
|
+
const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
|
|
2056
|
+
const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
|
|
2057
|
+
const record = {
|
|
2058
|
+
...span,
|
|
2059
|
+
startedAt,
|
|
2060
|
+
endedAt
|
|
2061
|
+
// Note: createdAt/updatedAt will be set by default values
|
|
2062
|
+
};
|
|
2063
|
+
return this.db.insert({ tableName: storage.TABLE_SPANS, record });
|
|
1938
2064
|
} catch (error$1) {
|
|
1939
|
-
await transaction.rollback();
|
|
1940
2065
|
throw new error.MastraError(
|
|
1941
2066
|
{
|
|
1942
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
2067
|
+
id: storage.createStorageErrorId("MSSQL", "CREATE_SPAN", "FAILED"),
|
|
1943
2068
|
domain: error.ErrorDomain.STORAGE,
|
|
1944
|
-
category: error.ErrorCategory.
|
|
2069
|
+
category: error.ErrorCategory.USER,
|
|
1945
2070
|
details: {
|
|
1946
|
-
|
|
1947
|
-
|
|
2071
|
+
spanId: span.spanId,
|
|
2072
|
+
traceId: span.traceId,
|
|
2073
|
+
spanType: span.spanType,
|
|
2074
|
+
spanName: span.name
|
|
1948
2075
|
}
|
|
1949
2076
|
},
|
|
1950
2077
|
error$1
|
|
1951
2078
|
);
|
|
1952
2079
|
}
|
|
1953
2080
|
}
|
|
1954
|
-
|
|
1955
|
-
* Create a new index on a table
|
|
1956
|
-
*/
|
|
1957
|
-
async createIndex(options) {
|
|
2081
|
+
async getTrace(traceId) {
|
|
1958
2082
|
try {
|
|
1959
|
-
const
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
indexName: table,
|
|
1963
|
-
schemaName: getSchemaName(this.schemaName)
|
|
2083
|
+
const tableName = getTableName2({
|
|
2084
|
+
indexName: storage.TABLE_SPANS,
|
|
2085
|
+
schemaName: getSchemaName2(this.schema)
|
|
1964
2086
|
});
|
|
1965
|
-
const
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
FROM
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
`);
|
|
1979
|
-
if (indexExists.recordset && indexExists.recordset.length > 0) {
|
|
1980
|
-
return;
|
|
2087
|
+
const request = this.pool.request();
|
|
2088
|
+
request.input("traceId", traceId);
|
|
2089
|
+
const result = await request.query(
|
|
2090
|
+
`SELECT
|
|
2091
|
+
[traceId], [spanId], [parentSpanId], [name], [scope], [spanType],
|
|
2092
|
+
[attributes], [metadata], [links], [input], [output], [error], [isEvent],
|
|
2093
|
+
[startedAt], [endedAt], [createdAt], [updatedAt]
|
|
2094
|
+
FROM ${tableName}
|
|
2095
|
+
WHERE [traceId] = @traceId
|
|
2096
|
+
ORDER BY [startedAt] DESC`
|
|
2097
|
+
);
|
|
2098
|
+
if (!result.recordset || result.recordset.length === 0) {
|
|
2099
|
+
return null;
|
|
1981
2100
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
}
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
return `[${utils.parseSqlIdentifier(col, "column name")}]`;
|
|
1992
|
-
}).join(", ");
|
|
1993
|
-
const whereStr = where ? ` WHERE ${where}` : "";
|
|
1994
|
-
const createIndexSql = `CREATE ${uniqueStr}INDEX [${indexNameSafe}] ON ${fullTableName} (${columnsStr})${whereStr}`;
|
|
1995
|
-
await this.pool.request().query(createIndexSql);
|
|
2101
|
+
return {
|
|
2102
|
+
traceId,
|
|
2103
|
+
spans: result.recordset.map(
|
|
2104
|
+
(span) => transformFromSqlRow({
|
|
2105
|
+
tableName: storage.TABLE_SPANS,
|
|
2106
|
+
sqlRow: span
|
|
2107
|
+
})
|
|
2108
|
+
)
|
|
2109
|
+
};
|
|
1996
2110
|
} catch (error$1) {
|
|
1997
2111
|
throw new error.MastraError(
|
|
1998
2112
|
{
|
|
1999
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
2113
|
+
id: storage.createStorageErrorId("MSSQL", "GET_TRACE", "FAILED"),
|
|
2000
2114
|
domain: error.ErrorDomain.STORAGE,
|
|
2001
|
-
category: error.ErrorCategory.
|
|
2115
|
+
category: error.ErrorCategory.USER,
|
|
2002
2116
|
details: {
|
|
2003
|
-
|
|
2004
|
-
tableName: options.table
|
|
2117
|
+
traceId
|
|
2005
2118
|
}
|
|
2006
2119
|
},
|
|
2007
2120
|
error$1
|
|
2008
2121
|
);
|
|
2009
2122
|
}
|
|
2010
2123
|
}
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2124
|
+
async updateSpan({
|
|
2125
|
+
spanId,
|
|
2126
|
+
traceId,
|
|
2127
|
+
updates
|
|
2128
|
+
}) {
|
|
2015
2129
|
try {
|
|
2016
|
-
const
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
checkRequest.input("indexName", indexNameSafe);
|
|
2020
|
-
checkRequest.input("schemaName", schemaName);
|
|
2021
|
-
const result = await checkRequest.query(`
|
|
2022
|
-
SELECT t.name as table_name
|
|
2023
|
-
FROM sys.indexes i
|
|
2024
|
-
INNER JOIN sys.tables t ON i.object_id = t.object_id
|
|
2025
|
-
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
2026
|
-
WHERE i.name = @indexName
|
|
2027
|
-
AND s.name = @schemaName
|
|
2028
|
-
`);
|
|
2029
|
-
if (!result.recordset || result.recordset.length === 0) {
|
|
2030
|
-
return;
|
|
2130
|
+
const data = { ...updates };
|
|
2131
|
+
if (data.endedAt instanceof Date) {
|
|
2132
|
+
data.endedAt = data.endedAt.toISOString();
|
|
2031
2133
|
}
|
|
2032
|
-
if (
|
|
2033
|
-
|
|
2034
|
-
throw new error.MastraError({
|
|
2035
|
-
id: storage.createStorageErrorId("MSSQL", "INDEX", "AMBIGUOUS"),
|
|
2036
|
-
domain: error.ErrorDomain.STORAGE,
|
|
2037
|
-
category: error.ErrorCategory.USER,
|
|
2038
|
-
text: `Index "${indexNameSafe}" exists on multiple tables (${tables}) in schema "${schemaName}". Please drop indexes manually or ensure unique index names.`
|
|
2039
|
-
});
|
|
2134
|
+
if (data.startedAt instanceof Date) {
|
|
2135
|
+
data.startedAt = data.startedAt.toISOString();
|
|
2040
2136
|
}
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2137
|
+
await this.db.update({
|
|
2138
|
+
tableName: storage.TABLE_SPANS,
|
|
2139
|
+
keys: { spanId, traceId },
|
|
2140
|
+
data
|
|
2045
2141
|
});
|
|
2046
|
-
const dropSql = `DROP INDEX [${indexNameSafe}] ON ${fullTableName}`;
|
|
2047
|
-
await this.pool.request().query(dropSql);
|
|
2048
2142
|
} catch (error$1) {
|
|
2049
2143
|
throw new error.MastraError(
|
|
2050
2144
|
{
|
|
2051
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
2145
|
+
id: storage.createStorageErrorId("MSSQL", "UPDATE_SPAN", "FAILED"),
|
|
2052
2146
|
domain: error.ErrorDomain.STORAGE,
|
|
2053
|
-
category: error.ErrorCategory.
|
|
2147
|
+
category: error.ErrorCategory.USER,
|
|
2054
2148
|
details: {
|
|
2055
|
-
|
|
2149
|
+
spanId,
|
|
2150
|
+
traceId
|
|
2056
2151
|
}
|
|
2057
2152
|
},
|
|
2058
2153
|
error$1
|
|
2059
2154
|
);
|
|
2060
2155
|
}
|
|
2061
2156
|
}
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
GROUP BY i.name, o.name, i.is_unique
|
|
2086
|
-
`;
|
|
2087
|
-
request.input("tableName", tableName);
|
|
2157
|
+
async getTracesPaginated({
|
|
2158
|
+
filters,
|
|
2159
|
+
pagination
|
|
2160
|
+
}) {
|
|
2161
|
+
const page = pagination?.page ?? 0;
|
|
2162
|
+
const perPage = pagination?.perPage ?? 10;
|
|
2163
|
+
const { entityId, entityType, ...actualFilters } = filters || {};
|
|
2164
|
+
const filtersWithDateRange = {
|
|
2165
|
+
...actualFilters,
|
|
2166
|
+
...buildDateRangeFilter(pagination?.dateRange, "startedAt"),
|
|
2167
|
+
parentSpanId: null
|
|
2168
|
+
// Only get root spans for traces
|
|
2169
|
+
};
|
|
2170
|
+
const whereClause = prepareWhereClause(filtersWithDateRange);
|
|
2171
|
+
let actualWhereClause = whereClause.sql;
|
|
2172
|
+
const params = { ...whereClause.params };
|
|
2173
|
+
let currentParamIndex = Object.keys(params).length + 1;
|
|
2174
|
+
if (entityId && entityType) {
|
|
2175
|
+
let name = "";
|
|
2176
|
+
if (entityType === "workflow") {
|
|
2177
|
+
name = `workflow run: '${entityId}'`;
|
|
2178
|
+
} else if (entityType === "agent") {
|
|
2179
|
+
name = `agent run: '${entityId}'`;
|
|
2088
2180
|
} else {
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2098
|
-
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2099
|
-
WHERE sch.name = @schemaName
|
|
2100
|
-
AND i.name IS NOT NULL
|
|
2101
|
-
GROUP BY i.name, o.name, i.is_unique
|
|
2102
|
-
`;
|
|
2103
|
-
}
|
|
2104
|
-
const result = await request.query(query);
|
|
2105
|
-
const indexes = [];
|
|
2106
|
-
for (const row of result.recordset) {
|
|
2107
|
-
const colRequest = this.pool.request();
|
|
2108
|
-
colRequest.input("indexName", row.name);
|
|
2109
|
-
colRequest.input("schemaName", schemaName);
|
|
2110
|
-
const colResult = await colRequest.query(`
|
|
2111
|
-
SELECT c.name as column_name
|
|
2112
|
-
FROM sys.indexes i
|
|
2113
|
-
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
2114
|
-
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
2115
|
-
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2116
|
-
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
2117
|
-
WHERE i.name = @indexName
|
|
2118
|
-
AND s.name = @schemaName
|
|
2119
|
-
ORDER BY ic.key_ordinal
|
|
2120
|
-
`);
|
|
2121
|
-
indexes.push({
|
|
2122
|
-
name: row.name,
|
|
2123
|
-
table: row.table,
|
|
2124
|
-
columns: colResult.recordset.map((c) => c.column_name),
|
|
2125
|
-
unique: row.is_unique || false,
|
|
2126
|
-
size: row.size || "0 MB",
|
|
2127
|
-
definition: ""
|
|
2128
|
-
// MSSQL doesn't store definition like PG
|
|
2181
|
+
const error$1 = new error.MastraError({
|
|
2182
|
+
id: storage.createStorageErrorId("MSSQL", "GET_TRACES_PAGINATED", "INVALID_ENTITY_TYPE"),
|
|
2183
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2184
|
+
category: error.ErrorCategory.USER,
|
|
2185
|
+
details: {
|
|
2186
|
+
entityType
|
|
2187
|
+
},
|
|
2188
|
+
text: `Cannot filter by entity type: ${entityType}`
|
|
2129
2189
|
});
|
|
2190
|
+
throw error$1;
|
|
2130
2191
|
}
|
|
2131
|
-
|
|
2192
|
+
const entityParam = `p${currentParamIndex++}`;
|
|
2193
|
+
if (actualWhereClause) {
|
|
2194
|
+
actualWhereClause += ` AND [name] = @${entityParam}`;
|
|
2195
|
+
} else {
|
|
2196
|
+
actualWhereClause = ` WHERE [name] = @${entityParam}`;
|
|
2197
|
+
}
|
|
2198
|
+
params[entityParam] = name;
|
|
2199
|
+
}
|
|
2200
|
+
const tableName = getTableName2({
|
|
2201
|
+
indexName: storage.TABLE_SPANS,
|
|
2202
|
+
schemaName: getSchemaName2(this.schema)
|
|
2203
|
+
});
|
|
2204
|
+
try {
|
|
2205
|
+
const countRequest = this.pool.request();
|
|
2206
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2207
|
+
countRequest.input(key, value);
|
|
2208
|
+
});
|
|
2209
|
+
const countResult = await countRequest.query(
|
|
2210
|
+
`SELECT COUNT(*) as count FROM ${tableName}${actualWhereClause}`
|
|
2211
|
+
);
|
|
2212
|
+
const total = countResult.recordset[0]?.count ?? 0;
|
|
2213
|
+
if (total === 0) {
|
|
2214
|
+
return {
|
|
2215
|
+
pagination: {
|
|
2216
|
+
total: 0,
|
|
2217
|
+
page,
|
|
2218
|
+
perPage,
|
|
2219
|
+
hasMore: false
|
|
2220
|
+
},
|
|
2221
|
+
spans: []
|
|
2222
|
+
};
|
|
2223
|
+
}
|
|
2224
|
+
const dataRequest = this.pool.request();
|
|
2225
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
2226
|
+
dataRequest.input(key, value);
|
|
2227
|
+
});
|
|
2228
|
+
dataRequest.input("offset", page * perPage);
|
|
2229
|
+
dataRequest.input("limit", perPage);
|
|
2230
|
+
const dataResult = await dataRequest.query(
|
|
2231
|
+
`SELECT * FROM ${tableName}${actualWhereClause} ORDER BY [startedAt] DESC OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY`
|
|
2232
|
+
);
|
|
2233
|
+
const spans = dataResult.recordset.map(
|
|
2234
|
+
(row) => transformFromSqlRow({
|
|
2235
|
+
tableName: storage.TABLE_SPANS,
|
|
2236
|
+
sqlRow: row
|
|
2237
|
+
})
|
|
2238
|
+
);
|
|
2239
|
+
return {
|
|
2240
|
+
pagination: {
|
|
2241
|
+
total,
|
|
2242
|
+
page,
|
|
2243
|
+
perPage,
|
|
2244
|
+
hasMore: (page + 1) * perPage < total
|
|
2245
|
+
},
|
|
2246
|
+
spans
|
|
2247
|
+
};
|
|
2132
2248
|
} catch (error$1) {
|
|
2133
2249
|
throw new error.MastraError(
|
|
2134
2250
|
{
|
|
2135
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
2251
|
+
id: storage.createStorageErrorId("MSSQL", "GET_TRACES_PAGINATED", "FAILED"),
|
|
2136
2252
|
domain: error.ErrorDomain.STORAGE,
|
|
2137
|
-
category: error.ErrorCategory.
|
|
2138
|
-
details: tableName ? {
|
|
2139
|
-
tableName
|
|
2140
|
-
} : {}
|
|
2253
|
+
category: error.ErrorCategory.USER
|
|
2141
2254
|
},
|
|
2142
2255
|
error$1
|
|
2143
2256
|
);
|
|
2144
2257
|
}
|
|
2145
2258
|
}
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2259
|
+
async batchCreateSpans(args) {
|
|
2260
|
+
if (!args.records || args.records.length === 0) {
|
|
2261
|
+
return;
|
|
2262
|
+
}
|
|
2150
2263
|
try {
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
i.is_unique as is_unique,
|
|
2160
|
-
CAST(SUM(s.used_page_count) * 8 / 1024.0 AS VARCHAR(50)) + ' MB' as size,
|
|
2161
|
-
i.type_desc as method,
|
|
2162
|
-
ISNULL(us.user_scans, 0) as scans,
|
|
2163
|
-
ISNULL(us.user_seeks + us.user_scans, 0) as tuples_read,
|
|
2164
|
-
ISNULL(us.user_lookups, 0) as tuples_fetched
|
|
2165
|
-
FROM sys.indexes i
|
|
2166
|
-
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2167
|
-
INNER JOIN sys.schemas sch ON o.schema_id = sch.schema_id
|
|
2168
|
-
LEFT JOIN sys.dm_db_partition_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
|
|
2169
|
-
LEFT JOIN sys.dm_db_index_usage_stats us ON i.object_id = us.object_id AND i.index_id = us.index_id
|
|
2170
|
-
WHERE i.name = @indexName
|
|
2171
|
-
AND sch.name = @schemaName
|
|
2172
|
-
GROUP BY i.name, o.name, i.is_unique, i.type_desc, us.user_seeks, us.user_scans, us.user_lookups
|
|
2173
|
-
`;
|
|
2174
|
-
const result = await request.query(query);
|
|
2175
|
-
if (!result.recordset || result.recordset.length === 0) {
|
|
2176
|
-
throw new Error(`Index "${indexName}" not found in schema "${schemaName}"`);
|
|
2177
|
-
}
|
|
2178
|
-
const row = result.recordset[0];
|
|
2179
|
-
const colRequest = this.pool.request();
|
|
2180
|
-
colRequest.input("indexName", indexName);
|
|
2181
|
-
colRequest.input("schemaName", schemaName);
|
|
2182
|
-
const colResult = await colRequest.query(`
|
|
2183
|
-
SELECT c.name as column_name
|
|
2184
|
-
FROM sys.indexes i
|
|
2185
|
-
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
2186
|
-
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
|
2187
|
-
INNER JOIN sys.objects o ON i.object_id = o.object_id
|
|
2188
|
-
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
2189
|
-
WHERE i.name = @indexName
|
|
2190
|
-
AND s.name = @schemaName
|
|
2191
|
-
ORDER BY ic.key_ordinal
|
|
2192
|
-
`);
|
|
2193
|
-
return {
|
|
2194
|
-
name: row.name,
|
|
2195
|
-
table: row.table,
|
|
2196
|
-
columns: colResult.recordset.map((c) => c.column_name),
|
|
2197
|
-
unique: row.is_unique || false,
|
|
2198
|
-
size: row.size || "0 MB",
|
|
2199
|
-
definition: "",
|
|
2200
|
-
method: row.method?.toLowerCase() || "nonclustered",
|
|
2201
|
-
scans: Number(row.scans) || 0,
|
|
2202
|
-
tuples_read: Number(row.tuples_read) || 0,
|
|
2203
|
-
tuples_fetched: Number(row.tuples_fetched) || 0
|
|
2204
|
-
};
|
|
2264
|
+
await this.db.batchInsert({
|
|
2265
|
+
tableName: storage.TABLE_SPANS,
|
|
2266
|
+
records: args.records.map((span) => ({
|
|
2267
|
+
...span,
|
|
2268
|
+
startedAt: span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt,
|
|
2269
|
+
endedAt: span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt
|
|
2270
|
+
}))
|
|
2271
|
+
});
|
|
2205
2272
|
} catch (error$1) {
|
|
2206
2273
|
throw new error.MastraError(
|
|
2207
2274
|
{
|
|
2208
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
2275
|
+
id: storage.createStorageErrorId("MSSQL", "BATCH_CREATE_SPANS", "FAILED"),
|
|
2209
2276
|
domain: error.ErrorDomain.STORAGE,
|
|
2210
|
-
category: error.ErrorCategory.
|
|
2277
|
+
category: error.ErrorCategory.USER,
|
|
2211
2278
|
details: {
|
|
2212
|
-
|
|
2279
|
+
count: args.records.length
|
|
2213
2280
|
}
|
|
2214
2281
|
},
|
|
2215
2282
|
error$1
|
|
2216
2283
|
);
|
|
2217
2284
|
}
|
|
2218
2285
|
}
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
*/
|
|
2224
|
-
getAutomaticIndexDefinitions() {
|
|
2225
|
-
const schemaPrefix = this.schemaName ? `${this.schemaName}_` : "";
|
|
2226
|
-
return [
|
|
2227
|
-
// Composite indexes for optimal filtering + sorting performance
|
|
2228
|
-
// NVARCHAR(400) = 800 bytes, plus BIGINT (8 bytes) = 808 bytes total (under 900-byte limit)
|
|
2229
|
-
{
|
|
2230
|
-
name: `${schemaPrefix}mastra_threads_resourceid_seqid_idx`,
|
|
2231
|
-
table: storage.TABLE_THREADS,
|
|
2232
|
-
columns: ["resourceId", "seq_id DESC"]
|
|
2233
|
-
},
|
|
2234
|
-
{
|
|
2235
|
-
name: `${schemaPrefix}mastra_messages_thread_id_seqid_idx`,
|
|
2236
|
-
table: storage.TABLE_MESSAGES,
|
|
2237
|
-
columns: ["thread_id", "seq_id DESC"]
|
|
2238
|
-
},
|
|
2239
|
-
{
|
|
2240
|
-
name: `${schemaPrefix}mastra_traces_name_seqid_idx`,
|
|
2241
|
-
table: storage.TABLE_TRACES,
|
|
2242
|
-
columns: ["name", "seq_id DESC"]
|
|
2243
|
-
},
|
|
2244
|
-
{
|
|
2245
|
-
name: `${schemaPrefix}mastra_scores_trace_id_span_id_seqid_idx`,
|
|
2246
|
-
table: storage.TABLE_SCORERS,
|
|
2247
|
-
columns: ["traceId", "spanId", "seq_id DESC"]
|
|
2248
|
-
},
|
|
2249
|
-
// Spans indexes for optimal trace querying
|
|
2250
|
-
{
|
|
2251
|
-
name: `${schemaPrefix}mastra_ai_spans_traceid_startedat_idx`,
|
|
2252
|
-
table: storage.TABLE_SPANS,
|
|
2253
|
-
columns: ["traceId", "startedAt DESC"]
|
|
2254
|
-
},
|
|
2255
|
-
{
|
|
2256
|
-
name: `${schemaPrefix}mastra_ai_spans_parentspanid_startedat_idx`,
|
|
2257
|
-
table: storage.TABLE_SPANS,
|
|
2258
|
-
columns: ["parentSpanId", "startedAt DESC"]
|
|
2259
|
-
},
|
|
2260
|
-
{
|
|
2261
|
-
name: `${schemaPrefix}mastra_ai_spans_name_idx`,
|
|
2262
|
-
table: storage.TABLE_SPANS,
|
|
2263
|
-
columns: ["name"]
|
|
2264
|
-
},
|
|
2265
|
-
{
|
|
2266
|
-
name: `${schemaPrefix}mastra_ai_spans_spantype_startedat_idx`,
|
|
2267
|
-
table: storage.TABLE_SPANS,
|
|
2268
|
-
columns: ["spanType", "startedAt DESC"]
|
|
2269
|
-
}
|
|
2270
|
-
];
|
|
2271
|
-
}
|
|
2272
|
-
/**
|
|
2273
|
-
* Creates automatic indexes for optimal query performance
|
|
2274
|
-
* Uses getAutomaticIndexDefinitions() to determine which indexes to create
|
|
2275
|
-
*/
|
|
2276
|
-
async createAutomaticIndexes() {
|
|
2286
|
+
async batchUpdateSpans(args) {
|
|
2287
|
+
if (!args.records || args.records.length === 0) {
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2277
2290
|
try {
|
|
2278
|
-
const
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
} catch (error) {
|
|
2283
|
-
this.logger?.warn?.(`Failed to create index ${indexOptions.name}:`, error);
|
|
2291
|
+
const updates = args.records.map(({ traceId, spanId, updates: data }) => {
|
|
2292
|
+
const processedData = { ...data };
|
|
2293
|
+
if (processedData.endedAt instanceof Date) {
|
|
2294
|
+
processedData.endedAt = processedData.endedAt.toISOString();
|
|
2284
2295
|
}
|
|
2285
|
-
|
|
2296
|
+
if (processedData.startedAt instanceof Date) {
|
|
2297
|
+
processedData.startedAt = processedData.startedAt.toISOString();
|
|
2298
|
+
}
|
|
2299
|
+
return {
|
|
2300
|
+
keys: { spanId, traceId },
|
|
2301
|
+
data: processedData
|
|
2302
|
+
};
|
|
2303
|
+
});
|
|
2304
|
+
await this.db.batchUpdate({
|
|
2305
|
+
tableName: storage.TABLE_SPANS,
|
|
2306
|
+
updates
|
|
2307
|
+
});
|
|
2286
2308
|
} catch (error$1) {
|
|
2287
2309
|
throw new error.MastraError(
|
|
2288
2310
|
{
|
|
2289
|
-
id: storage.createStorageErrorId("MSSQL", "
|
|
2311
|
+
id: storage.createStorageErrorId("MSSQL", "BATCH_UPDATE_SPANS", "FAILED"),
|
|
2290
2312
|
domain: error.ErrorDomain.STORAGE,
|
|
2291
|
-
category: error.ErrorCategory.
|
|
2313
|
+
category: error.ErrorCategory.USER,
|
|
2314
|
+
details: {
|
|
2315
|
+
count: args.records.length
|
|
2316
|
+
}
|
|
2317
|
+
},
|
|
2318
|
+
error$1
|
|
2319
|
+
);
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
async batchDeleteTraces(args) {
|
|
2323
|
+
if (!args.traceIds || args.traceIds.length === 0) {
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2326
|
+
try {
|
|
2327
|
+
const keys = args.traceIds.map((traceId) => ({ traceId }));
|
|
2328
|
+
await this.db.batchDelete({
|
|
2329
|
+
tableName: storage.TABLE_SPANS,
|
|
2330
|
+
keys
|
|
2331
|
+
});
|
|
2332
|
+
} catch (error$1) {
|
|
2333
|
+
throw new error.MastraError(
|
|
2334
|
+
{
|
|
2335
|
+
id: storage.createStorageErrorId("MSSQL", "BATCH_DELETE_TRACES", "FAILED"),
|
|
2336
|
+
domain: error.ErrorDomain.STORAGE,
|
|
2337
|
+
category: error.ErrorCategory.USER,
|
|
2338
|
+
details: {
|
|
2339
|
+
count: args.traceIds.length
|
|
2340
|
+
}
|
|
2292
2341
|
},
|
|
2293
2342
|
error$1
|
|
2294
2343
|
);
|
|
@@ -2302,24 +2351,33 @@ function transformScoreRow(row) {
|
|
|
2302
2351
|
}
|
|
2303
2352
|
var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
2304
2353
|
pool;
|
|
2305
|
-
|
|
2354
|
+
db;
|
|
2306
2355
|
schema;
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
operations,
|
|
2310
|
-
schema
|
|
2311
|
-
}) {
|
|
2356
|
+
needsConnect;
|
|
2357
|
+
constructor(config) {
|
|
2312
2358
|
super();
|
|
2359
|
+
const { pool, db, schema, needsConnect } = resolveMssqlConfig(config);
|
|
2313
2360
|
this.pool = pool;
|
|
2314
|
-
this.
|
|
2361
|
+
this.db = db;
|
|
2315
2362
|
this.schema = schema;
|
|
2363
|
+
this.needsConnect = needsConnect;
|
|
2364
|
+
}
|
|
2365
|
+
async init() {
|
|
2366
|
+
if (this.needsConnect) {
|
|
2367
|
+
await this.pool.connect();
|
|
2368
|
+
this.needsConnect = false;
|
|
2369
|
+
}
|
|
2370
|
+
await this.db.createTable({ tableName: storage.TABLE_SCORERS, schema: storage.TABLE_SCHEMAS[storage.TABLE_SCORERS] });
|
|
2371
|
+
}
|
|
2372
|
+
async dangerouslyClearAll() {
|
|
2373
|
+
await this.db.clearTable({ tableName: storage.TABLE_SCORERS });
|
|
2316
2374
|
}
|
|
2317
2375
|
async getScoreById({ id }) {
|
|
2318
2376
|
try {
|
|
2319
2377
|
const request = this.pool.request();
|
|
2320
2378
|
request.input("p1", id);
|
|
2321
2379
|
const result = await request.query(
|
|
2322
|
-
`SELECT * FROM ${
|
|
2380
|
+
`SELECT * FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE id = @p1`
|
|
2323
2381
|
);
|
|
2324
2382
|
if (result.recordset.length === 0) {
|
|
2325
2383
|
return null;
|
|
@@ -2373,7 +2431,7 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
2373
2431
|
entity,
|
|
2374
2432
|
...rest
|
|
2375
2433
|
} = validatedScore;
|
|
2376
|
-
await this.
|
|
2434
|
+
await this.db.insert({
|
|
2377
2435
|
tableName: storage.TABLE_SCORERS,
|
|
2378
2436
|
record: {
|
|
2379
2437
|
id: scoreId,
|
|
@@ -2430,7 +2488,7 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
2430
2488
|
paramIndex++;
|
|
2431
2489
|
}
|
|
2432
2490
|
const whereClause = conditions.join(" AND ");
|
|
2433
|
-
const tableName =
|
|
2491
|
+
const tableName = getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.schema) });
|
|
2434
2492
|
const countRequest = this.pool.request();
|
|
2435
2493
|
Object.entries(params).forEach(([key, value]) => {
|
|
2436
2494
|
countRequest.input(key, value);
|
|
@@ -2490,7 +2548,7 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
2490
2548
|
const request = this.pool.request();
|
|
2491
2549
|
request.input("p1", runId);
|
|
2492
2550
|
const totalResult = await request.query(
|
|
2493
|
-
`SELECT COUNT(*) as count FROM ${
|
|
2551
|
+
`SELECT COUNT(*) as count FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE [runId] = @p1`
|
|
2494
2552
|
);
|
|
2495
2553
|
const total = totalResult.recordset[0]?.count || 0;
|
|
2496
2554
|
const { page, perPage: perPageInput } = pagination;
|
|
@@ -2514,7 +2572,7 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
2514
2572
|
dataRequest.input("p2", limitValue);
|
|
2515
2573
|
dataRequest.input("p3", start);
|
|
2516
2574
|
const result = await dataRequest.query(
|
|
2517
|
-
`SELECT * FROM ${
|
|
2575
|
+
`SELECT * FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE [runId] = @p1 ORDER BY [createdAt] DESC OFFSET @p3 ROWS FETCH NEXT @p2 ROWS ONLY`
|
|
2518
2576
|
);
|
|
2519
2577
|
return {
|
|
2520
2578
|
pagination: {
|
|
@@ -2547,7 +2605,7 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
2547
2605
|
request.input("p1", entityId);
|
|
2548
2606
|
request.input("p2", entityType);
|
|
2549
2607
|
const totalResult = await request.query(
|
|
2550
|
-
`SELECT COUNT(*) as count FROM ${
|
|
2608
|
+
`SELECT COUNT(*) as count FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE [entityId] = @p1 AND [entityType] = @p2`
|
|
2551
2609
|
);
|
|
2552
2610
|
const total = totalResult.recordset[0]?.count || 0;
|
|
2553
2611
|
const { page, perPage: perPageInput } = pagination;
|
|
@@ -2572,7 +2630,7 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
2572
2630
|
dataRequest.input("p3", limitValue);
|
|
2573
2631
|
dataRequest.input("p4", start);
|
|
2574
2632
|
const result = await dataRequest.query(
|
|
2575
|
-
`SELECT * FROM ${
|
|
2633
|
+
`SELECT * FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE [entityId] = @p1 AND [entityType] = @p2 ORDER BY [createdAt] DESC OFFSET @p4 ROWS FETCH NEXT @p3 ROWS ONLY`
|
|
2576
2634
|
);
|
|
2577
2635
|
return {
|
|
2578
2636
|
pagination: {
|
|
@@ -2605,7 +2663,7 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
2605
2663
|
request.input("p1", traceId);
|
|
2606
2664
|
request.input("p2", spanId);
|
|
2607
2665
|
const totalResult = await request.query(
|
|
2608
|
-
`SELECT COUNT(*) as count FROM ${
|
|
2666
|
+
`SELECT COUNT(*) as count FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE [traceId] = @p1 AND [spanId] = @p2`
|
|
2609
2667
|
);
|
|
2610
2668
|
const total = totalResult.recordset[0]?.count || 0;
|
|
2611
2669
|
const { page, perPage: perPageInput } = pagination;
|
|
@@ -2630,7 +2688,7 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
2630
2688
|
dataRequest.input("p3", limitValue);
|
|
2631
2689
|
dataRequest.input("p4", start);
|
|
2632
2690
|
const result = await dataRequest.query(
|
|
2633
|
-
`SELECT * FROM ${
|
|
2691
|
+
`SELECT * FROM ${getTableName2({ indexName: storage.TABLE_SCORERS, schemaName: getSchemaName2(this.schema) })} WHERE [traceId] = @p1 AND [spanId] = @p2 ORDER BY [createdAt] DESC OFFSET @p4 ROWS FETCH NEXT @p3 ROWS ONLY`
|
|
2634
2692
|
);
|
|
2635
2693
|
return {
|
|
2636
2694
|
pagination: {
|
|
@@ -2656,17 +2714,26 @@ var ScoresMSSQL = class extends storage.ScoresStorage {
|
|
|
2656
2714
|
};
|
|
2657
2715
|
var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
2658
2716
|
pool;
|
|
2659
|
-
|
|
2717
|
+
db;
|
|
2660
2718
|
schema;
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
operations,
|
|
2664
|
-
schema
|
|
2665
|
-
}) {
|
|
2719
|
+
needsConnect;
|
|
2720
|
+
constructor(config) {
|
|
2666
2721
|
super();
|
|
2722
|
+
const { pool, db, schema, needsConnect } = resolveMssqlConfig(config);
|
|
2667
2723
|
this.pool = pool;
|
|
2668
|
-
this.
|
|
2724
|
+
this.db = db;
|
|
2669
2725
|
this.schema = schema;
|
|
2726
|
+
this.needsConnect = needsConnect;
|
|
2727
|
+
}
|
|
2728
|
+
async init() {
|
|
2729
|
+
if (this.needsConnect) {
|
|
2730
|
+
await this.pool.connect();
|
|
2731
|
+
this.needsConnect = false;
|
|
2732
|
+
}
|
|
2733
|
+
await this.db.createTable({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT, schema: storage.TABLE_SCHEMAS[storage.TABLE_WORKFLOW_SNAPSHOT] });
|
|
2734
|
+
}
|
|
2735
|
+
async dangerouslyClearAll() {
|
|
2736
|
+
await this.db.clearTable({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT });
|
|
2670
2737
|
}
|
|
2671
2738
|
parseWorkflowRun(row) {
|
|
2672
2739
|
let parsedSnapshot = row.snapshot;
|
|
@@ -2693,11 +2760,11 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
2693
2760
|
result,
|
|
2694
2761
|
requestContext
|
|
2695
2762
|
}) {
|
|
2696
|
-
const table =
|
|
2763
|
+
const table = getTableName2({ indexName: storage.TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.schema) });
|
|
2697
2764
|
const transaction = this.pool.transaction();
|
|
2698
2765
|
try {
|
|
2699
2766
|
await transaction.begin();
|
|
2700
|
-
const selectRequest = new
|
|
2767
|
+
const selectRequest = new sql__default.default.Request(transaction);
|
|
2701
2768
|
selectRequest.input("workflow_name", workflowName);
|
|
2702
2769
|
selectRequest.input("run_id", runId);
|
|
2703
2770
|
const existingSnapshotResult = await selectRequest.query(
|
|
@@ -2725,12 +2792,12 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
2725
2792
|
}
|
|
2726
2793
|
snapshot.context[stepId] = result;
|
|
2727
2794
|
snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
|
|
2728
|
-
const upsertReq = new
|
|
2795
|
+
const upsertReq = new sql__default.default.Request(transaction);
|
|
2729
2796
|
upsertReq.input("workflow_name", workflowName);
|
|
2730
2797
|
upsertReq.input("run_id", runId);
|
|
2731
2798
|
upsertReq.input("snapshot", JSON.stringify(snapshot));
|
|
2732
|
-
upsertReq.input("createdAt",
|
|
2733
|
-
upsertReq.input("updatedAt",
|
|
2799
|
+
upsertReq.input("createdAt", sql__default.default.DateTime2, /* @__PURE__ */ new Date());
|
|
2800
|
+
upsertReq.input("updatedAt", sql__default.default.DateTime2, /* @__PURE__ */ new Date());
|
|
2734
2801
|
await upsertReq.query(
|
|
2735
2802
|
`MERGE ${table} AS target
|
|
2736
2803
|
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
@@ -2766,11 +2833,11 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
2766
2833
|
runId,
|
|
2767
2834
|
opts
|
|
2768
2835
|
}) {
|
|
2769
|
-
const table =
|
|
2836
|
+
const table = getTableName2({ indexName: storage.TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.schema) });
|
|
2770
2837
|
const transaction = this.pool.transaction();
|
|
2771
2838
|
try {
|
|
2772
2839
|
await transaction.begin();
|
|
2773
|
-
const selectRequest = new
|
|
2840
|
+
const selectRequest = new sql__default.default.Request(transaction);
|
|
2774
2841
|
selectRequest.input("workflow_name", workflowName);
|
|
2775
2842
|
selectRequest.input("run_id", runId);
|
|
2776
2843
|
const existingSnapshotResult = await selectRequest.query(
|
|
@@ -2798,11 +2865,11 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
2798
2865
|
);
|
|
2799
2866
|
}
|
|
2800
2867
|
const updatedSnapshot = { ...snapshot, ...opts };
|
|
2801
|
-
const updateRequest = new
|
|
2868
|
+
const updateRequest = new sql__default.default.Request(transaction);
|
|
2802
2869
|
updateRequest.input("snapshot", JSON.stringify(updatedSnapshot));
|
|
2803
2870
|
updateRequest.input("workflow_name", workflowName);
|
|
2804
2871
|
updateRequest.input("run_id", runId);
|
|
2805
|
-
updateRequest.input("updatedAt",
|
|
2872
|
+
updateRequest.input("updatedAt", sql__default.default.DateTime2, /* @__PURE__ */ new Date());
|
|
2806
2873
|
await updateRequest.query(
|
|
2807
2874
|
`UPDATE ${table} SET snapshot = @snapshot, [updatedAt] = @updatedAt WHERE workflow_name = @workflow_name AND run_id = @run_id`
|
|
2808
2875
|
);
|
|
@@ -2834,7 +2901,7 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
2834
2901
|
resourceId,
|
|
2835
2902
|
snapshot
|
|
2836
2903
|
}) {
|
|
2837
|
-
const table =
|
|
2904
|
+
const table = getTableName2({ indexName: storage.TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.schema) });
|
|
2838
2905
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2839
2906
|
try {
|
|
2840
2907
|
const request = this.pool.request();
|
|
@@ -2842,8 +2909,8 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
2842
2909
|
request.input("run_id", runId);
|
|
2843
2910
|
request.input("resourceId", resourceId);
|
|
2844
2911
|
request.input("snapshot", JSON.stringify(snapshot));
|
|
2845
|
-
request.input("createdAt",
|
|
2846
|
-
request.input("updatedAt",
|
|
2912
|
+
request.input("createdAt", sql__default.default.DateTime2, new Date(now));
|
|
2913
|
+
request.input("updatedAt", sql__default.default.DateTime2, new Date(now));
|
|
2847
2914
|
const mergeSql = `MERGE INTO ${table} AS target
|
|
2848
2915
|
USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
|
|
2849
2916
|
ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
|
|
@@ -2874,7 +2941,7 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
2874
2941
|
runId
|
|
2875
2942
|
}) {
|
|
2876
2943
|
try {
|
|
2877
|
-
const result = await this.
|
|
2944
|
+
const result = await this.db.load({
|
|
2878
2945
|
tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
2879
2946
|
keys: {
|
|
2880
2947
|
workflow_name: workflowName,
|
|
@@ -2916,7 +2983,7 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
2916
2983
|
paramMap["workflowName"] = workflowName;
|
|
2917
2984
|
}
|
|
2918
2985
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2919
|
-
const tableName =
|
|
2986
|
+
const tableName = getTableName2({ indexName: storage.TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.schema) });
|
|
2920
2987
|
const query = `SELECT * FROM ${tableName} ${whereClause}`;
|
|
2921
2988
|
const request = this.pool.request();
|
|
2922
2989
|
Object.entries(paramMap).forEach(([key, value]) => request.input(key, value));
|
|
@@ -2941,11 +3008,11 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
2941
3008
|
}
|
|
2942
3009
|
}
|
|
2943
3010
|
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
2944
|
-
const table =
|
|
3011
|
+
const table = getTableName2({ indexName: storage.TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.schema) });
|
|
2945
3012
|
const transaction = this.pool.transaction();
|
|
2946
3013
|
try {
|
|
2947
3014
|
await transaction.begin();
|
|
2948
|
-
const deleteRequest = new
|
|
3015
|
+
const deleteRequest = new sql__default.default.Request(transaction);
|
|
2949
3016
|
deleteRequest.input("workflow_name", workflowName);
|
|
2950
3017
|
deleteRequest.input("run_id", runId);
|
|
2951
3018
|
await deleteRequest.query(`DELETE FROM ${table} WHERE workflow_name = @workflow_name AND run_id = @run_id`);
|
|
@@ -2990,7 +3057,7 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
2990
3057
|
paramMap["status"] = status;
|
|
2991
3058
|
}
|
|
2992
3059
|
if (resourceId) {
|
|
2993
|
-
const hasResourceId = await this.
|
|
3060
|
+
const hasResourceId = await this.db.hasColumn(storage.TABLE_WORKFLOW_SNAPSHOT, "resourceId");
|
|
2994
3061
|
if (hasResourceId) {
|
|
2995
3062
|
conditions.push(`[resourceId] = @resourceId`);
|
|
2996
3063
|
paramMap["resourceId"] = resourceId;
|
|
@@ -3008,11 +3075,11 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
3008
3075
|
}
|
|
3009
3076
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
3010
3077
|
let total = 0;
|
|
3011
|
-
const tableName =
|
|
3078
|
+
const tableName = getTableName2({ indexName: storage.TABLE_WORKFLOW_SNAPSHOT, schemaName: getSchemaName2(this.schema) });
|
|
3012
3079
|
const request = this.pool.request();
|
|
3013
3080
|
Object.entries(paramMap).forEach(([key, value]) => {
|
|
3014
3081
|
if (value instanceof Date) {
|
|
3015
|
-
request.input(key,
|
|
3082
|
+
request.input(key, sql__default.default.DateTime, value);
|
|
3016
3083
|
} else {
|
|
3017
3084
|
request.input(key, value);
|
|
3018
3085
|
}
|
|
@@ -3051,10 +3118,14 @@ var WorkflowsMSSQL = class extends storage.WorkflowsStorage {
|
|
|
3051
3118
|
};
|
|
3052
3119
|
|
|
3053
3120
|
// src/storage/index.ts
|
|
3121
|
+
var isPoolConfig = (config) => {
|
|
3122
|
+
return "pool" in config;
|
|
3123
|
+
};
|
|
3054
3124
|
var MSSQLStore = class extends storage.MastraStorage {
|
|
3055
3125
|
pool;
|
|
3056
3126
|
schema;
|
|
3057
3127
|
isConnected = null;
|
|
3128
|
+
#db;
|
|
3058
3129
|
stores;
|
|
3059
3130
|
constructor(config) {
|
|
3060
3131
|
if (!config.id || typeof config.id !== "string" || config.id.trim() === "") {
|
|
@@ -3062,10 +3133,14 @@ var MSSQLStore = class extends storage.MastraStorage {
|
|
|
3062
3133
|
}
|
|
3063
3134
|
super({ id: config.id, name: "MSSQLStore", disableInit: config.disableInit });
|
|
3064
3135
|
try {
|
|
3065
|
-
|
|
3136
|
+
this.schema = config.schemaName || "dbo";
|
|
3137
|
+
if (isPoolConfig(config)) {
|
|
3138
|
+
this.pool = config.pool;
|
|
3139
|
+
} else if ("connectionString" in config) {
|
|
3066
3140
|
if (!config.connectionString || typeof config.connectionString !== "string" || config.connectionString.trim() === "") {
|
|
3067
3141
|
throw new Error("MSSQLStore: connectionString must be provided and cannot be empty.");
|
|
3068
3142
|
}
|
|
3143
|
+
this.pool = new sql__default.default.ConnectionPool(config.connectionString);
|
|
3069
3144
|
} else {
|
|
3070
3145
|
const required = ["server", "database", "user", "password"];
|
|
3071
3146
|
for (const key of required) {
|
|
@@ -3073,23 +3148,22 @@ var MSSQLStore = class extends storage.MastraStorage {
|
|
|
3073
3148
|
throw new Error(`MSSQLStore: ${key} must be provided and cannot be empty.`);
|
|
3074
3149
|
}
|
|
3075
3150
|
}
|
|
3151
|
+
this.pool = new sql__default.default.ConnectionPool({
|
|
3152
|
+
server: config.server,
|
|
3153
|
+
database: config.database,
|
|
3154
|
+
user: config.user,
|
|
3155
|
+
password: config.password,
|
|
3156
|
+
port: config.port,
|
|
3157
|
+
options: config.options || { encrypt: true, trustServerCertificate: true }
|
|
3158
|
+
});
|
|
3076
3159
|
}
|
|
3077
|
-
this
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
port: config.port,
|
|
3084
|
-
options: config.options || { encrypt: true, trustServerCertificate: true }
|
|
3085
|
-
});
|
|
3086
|
-
const operations = new StoreOperationsMSSQL({ pool: this.pool, schemaName: this.schema });
|
|
3087
|
-
const scores = new ScoresMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
3088
|
-
const workflows = new WorkflowsMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
3089
|
-
const memory = new MemoryMSSQL({ pool: this.pool, schema: this.schema, operations });
|
|
3090
|
-
const observability = new ObservabilityMSSQL({ pool: this.pool, operations, schema: this.schema });
|
|
3160
|
+
this.#db = new MssqlDB({ pool: this.pool, schemaName: this.schema });
|
|
3161
|
+
const domainConfig = { pool: this.pool, db: this.#db, schema: this.schema };
|
|
3162
|
+
const scores = new ScoresMSSQL(domainConfig);
|
|
3163
|
+
const workflows = new WorkflowsMSSQL(domainConfig);
|
|
3164
|
+
const memory = new MemoryMSSQL(domainConfig);
|
|
3165
|
+
const observability = new ObservabilityMSSQL(domainConfig);
|
|
3091
3166
|
this.stores = {
|
|
3092
|
-
operations,
|
|
3093
3167
|
scores,
|
|
3094
3168
|
workflows,
|
|
3095
3169
|
memory,
|
|
@@ -3114,7 +3188,7 @@ var MSSQLStore = class extends storage.MastraStorage {
|
|
|
3114
3188
|
await this.isConnected;
|
|
3115
3189
|
await super.init();
|
|
3116
3190
|
try {
|
|
3117
|
-
await this.
|
|
3191
|
+
await this.#db.createAutomaticIndexes();
|
|
3118
3192
|
} catch (indexError) {
|
|
3119
3193
|
this.logger?.warn?.("Failed to create indexes:", indexError);
|
|
3120
3194
|
}
|
|
@@ -3150,34 +3224,6 @@ var MSSQLStore = class extends storage.MastraStorage {
|
|
|
3150
3224
|
indexManagement: true
|
|
3151
3225
|
};
|
|
3152
3226
|
}
|
|
3153
|
-
async createTable({
|
|
3154
|
-
tableName,
|
|
3155
|
-
schema
|
|
3156
|
-
}) {
|
|
3157
|
-
return this.stores.operations.createTable({ tableName, schema });
|
|
3158
|
-
}
|
|
3159
|
-
async alterTable({
|
|
3160
|
-
tableName,
|
|
3161
|
-
schema,
|
|
3162
|
-
ifNotExists
|
|
3163
|
-
}) {
|
|
3164
|
-
return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
|
|
3165
|
-
}
|
|
3166
|
-
async clearTable({ tableName }) {
|
|
3167
|
-
return this.stores.operations.clearTable({ tableName });
|
|
3168
|
-
}
|
|
3169
|
-
async dropTable({ tableName }) {
|
|
3170
|
-
return this.stores.operations.dropTable({ tableName });
|
|
3171
|
-
}
|
|
3172
|
-
async insert({ tableName, record }) {
|
|
3173
|
-
return this.stores.operations.insert({ tableName, record });
|
|
3174
|
-
}
|
|
3175
|
-
async batchInsert({ tableName, records }) {
|
|
3176
|
-
return this.stores.operations.batchInsert({ tableName, records });
|
|
3177
|
-
}
|
|
3178
|
-
async load({ tableName, keys }) {
|
|
3179
|
-
return this.stores.operations.load({ tableName, keys });
|
|
3180
|
-
}
|
|
3181
3227
|
/**
|
|
3182
3228
|
* Memory
|
|
3183
3229
|
*/
|
|
@@ -3276,16 +3322,16 @@ var MSSQLStore = class extends storage.MastraStorage {
|
|
|
3276
3322
|
* Index Management
|
|
3277
3323
|
*/
|
|
3278
3324
|
async createIndex(options) {
|
|
3279
|
-
return this.
|
|
3325
|
+
return this.#db.createIndex(options);
|
|
3280
3326
|
}
|
|
3281
3327
|
async listIndexes(tableName) {
|
|
3282
|
-
return this.
|
|
3328
|
+
return this.#db.listIndexes(tableName);
|
|
3283
3329
|
}
|
|
3284
3330
|
async describeIndex(indexName) {
|
|
3285
|
-
return this.
|
|
3331
|
+
return this.#db.describeIndex(indexName);
|
|
3286
3332
|
}
|
|
3287
3333
|
async dropIndex(indexName) {
|
|
3288
|
-
return this.
|
|
3334
|
+
return this.#db.dropIndex(indexName);
|
|
3289
3335
|
}
|
|
3290
3336
|
/**
|
|
3291
3337
|
* Tracing / Observability
|