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