@mastra/mysql 0.7.1-alpha.0 → 0.7.1-alpha.2
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 +30 -0
- package/dist/index.cjs +129 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +129 -27
- package/dist/index.js.map +1 -1
- package/dist/storage/db/schema-snapshot.d.ts +55 -0
- package/dist/storage/db/schema-snapshot.d.ts.map +1 -0
- package/dist/storage/domains/agents/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/operations/index.d.ts +16 -0
- package/dist/storage/domains/operations/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5,6 +5,46 @@ import { parseSqlIdentifier } from "@mastra/core/utils";
|
|
|
5
5
|
import { randomUUID } from "crypto";
|
|
6
6
|
import { MessageList } from "@mastra/core/agent";
|
|
7
7
|
import { skillSnapshotFieldValuesEqual } from "@mastra/core/storage/domains/skills";
|
|
8
|
+
//#region src/storage/db/schema-snapshot.ts
|
|
9
|
+
/** Builds the `table.index` presence key the snapshot uses. */
|
|
10
|
+
function indexKey(table, index) {
|
|
11
|
+
return `${table.toLowerCase()}.${index.toLowerCase()}`;
|
|
12
|
+
}
|
|
13
|
+
const lower = (value) => String(value).toLowerCase();
|
|
14
|
+
/**
|
|
15
|
+
* Reads the catalog for `schemaName` in three queries. Returns null when no
|
|
16
|
+
* schema name is available (a pool with no default database): correctness over
|
|
17
|
+
* optimization, callers fall back to today's per-probe behavior.
|
|
18
|
+
*/
|
|
19
|
+
async function loadSchemaSnapshot(pool, schemaName) {
|
|
20
|
+
if (!schemaName) return null;
|
|
21
|
+
const [[tableRows], [columnRows], [indexRows]] = await Promise.all([
|
|
22
|
+
pool.execute(`SELECT table_name FROM information_schema.tables WHERE table_schema = ?`, [schemaName]),
|
|
23
|
+
pool.execute(`SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = ?`, [schemaName]),
|
|
24
|
+
pool.execute(`SELECT DISTINCT table_name, index_name FROM information_schema.statistics WHERE table_schema = ?`, [schemaName])
|
|
25
|
+
]);
|
|
26
|
+
const tables = /* @__PURE__ */ new Set();
|
|
27
|
+
for (const row of tableRows ?? []) tables.add(lower(row.table_name ?? row.TABLE_NAME));
|
|
28
|
+
const columns = /* @__PURE__ */ new Map();
|
|
29
|
+
for (const row of columnRows ?? []) {
|
|
30
|
+
const table = lower(row.table_name ?? row.TABLE_NAME);
|
|
31
|
+
let set = columns.get(table);
|
|
32
|
+
if (!set) {
|
|
33
|
+
set = /* @__PURE__ */ new Set();
|
|
34
|
+
columns.set(table, set);
|
|
35
|
+
}
|
|
36
|
+
set.add(lower(row.column_name ?? row.COLUMN_NAME));
|
|
37
|
+
}
|
|
38
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
39
|
+
for (const row of indexRows ?? []) indexes.add(indexKey(lower(row.table_name ?? row.TABLE_NAME), lower(row.index_name ?? row.INDEX_NAME)));
|
|
40
|
+
return {
|
|
41
|
+
schemaName,
|
|
42
|
+
tables,
|
|
43
|
+
columns,
|
|
44
|
+
indexes
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
8
48
|
//#region src/storage/domains/utils.ts
|
|
9
49
|
function quoteIdentifier(value, context) {
|
|
10
50
|
return `\`${parseSqlIdentifier(value, context)}\``;
|
|
@@ -144,12 +184,38 @@ var StoreOperationsMySQL = class extends StoreOperations {
|
|
|
144
184
|
pool;
|
|
145
185
|
database;
|
|
146
186
|
resolvedDatabase;
|
|
187
|
+
/**
|
|
188
|
+
* Init-scoped catalog snapshot (see db/schema-snapshot.ts). Installed by
|
|
189
|
+
* MySQLStore.init() for exactly the init window and cleared in its finally;
|
|
190
|
+
* null at runtime so non-init callers keep probing the live catalog.
|
|
191
|
+
*/
|
|
192
|
+
schemaSnapshot = null;
|
|
147
193
|
constructor({ pool, database }) {
|
|
148
194
|
super();
|
|
149
195
|
this.pool = pool;
|
|
150
196
|
this.database = database;
|
|
151
197
|
this.resolvedDatabase = database ?? null;
|
|
152
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Loads and installs the init-scoped snapshot. A null load (no default
|
|
201
|
+
* database) or a failed load leaves the snapshot uninstalled, so init
|
|
202
|
+
* proceeds with today's per-probe behavior: correctness over optimization.
|
|
203
|
+
*/
|
|
204
|
+
async loadInitSchemaSnapshot() {
|
|
205
|
+
try {
|
|
206
|
+
this.schemaSnapshot = await loadSchemaSnapshot(this.pool, await this.getDatabase());
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.warn("Failed to load init schema snapshot, falling back to per-object probing:", error);
|
|
209
|
+
this.schemaSnapshot = null;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
clearInitSchemaSnapshot() {
|
|
213
|
+
this.schemaSnapshot = null;
|
|
214
|
+
}
|
|
215
|
+
/** Read by sibling domains (memory's raw index DDL) so snapshot state lives in exactly one place. */
|
|
216
|
+
getInitSchemaSnapshot() {
|
|
217
|
+
return this.schemaSnapshot;
|
|
218
|
+
}
|
|
153
219
|
getPool() {
|
|
154
220
|
return this.pool;
|
|
155
221
|
}
|
|
@@ -165,6 +231,8 @@ var StoreOperationsMySQL = class extends StoreOperations {
|
|
|
165
231
|
return this.resolvedDatabase ?? void 0;
|
|
166
232
|
}
|
|
167
233
|
async hasColumn(table, column) {
|
|
234
|
+
const snapshotColumns = this.schemaSnapshot?.columns.get(table.toLowerCase());
|
|
235
|
+
if (snapshotColumns) return snapshotColumns.has(column.toLowerCase());
|
|
168
236
|
const db = await this.getDatabase();
|
|
169
237
|
const params = [table, column];
|
|
170
238
|
let sql = "SELECT COUNT(*) as count FROM information_schema.columns WHERE table_name = ? AND (column_name = ? OR column_name = ? )";
|
|
@@ -242,13 +310,23 @@ var StoreOperationsMySQL = class extends StoreOperations {
|
|
|
242
310
|
return false;
|
|
243
311
|
}
|
|
244
312
|
async createTable({ tableName, schema }) {
|
|
313
|
+
const snapshot = this.schemaSnapshot;
|
|
314
|
+
if (snapshot?.tables.has(tableName.toLowerCase())) return;
|
|
245
315
|
const connection = await this.pool.getConnection();
|
|
246
316
|
try {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
317
|
+
if (!snapshot) {
|
|
318
|
+
const db = await this.getDatabase();
|
|
319
|
+
const [t_rows] = await connection.query("SELECT COUNT(*) AS count FROM information_schema.tables WHERE table_schema = ? AND table_name = ?", [db ?? "", tableName]);
|
|
320
|
+
if (Array.isArray(t_rows) && t_rows.length > 0 && t_rows[0].count > 0) return;
|
|
321
|
+
}
|
|
250
322
|
const sql = this.getCreateTableSQL(tableName, schema);
|
|
251
|
-
await connection.execute(sql);
|
|
323
|
+
const [result] = await connection.execute(sql);
|
|
324
|
+
const created = result?.warningStatus === 0;
|
|
325
|
+
if (snapshot && created) {
|
|
326
|
+
const table = tableName.toLowerCase();
|
|
327
|
+
snapshot.tables.add(table);
|
|
328
|
+
snapshot.columns.set(table, new Set(Object.keys(schema).map((column) => column.toLowerCase())));
|
|
329
|
+
}
|
|
252
330
|
} catch (error) {
|
|
253
331
|
throw new MastraError({
|
|
254
332
|
id: "MYSQL_STORE_CREATE_TABLE_FAILED",
|
|
@@ -304,17 +382,22 @@ var StoreOperationsMySQL = class extends StoreOperations {
|
|
|
304
382
|
const tableName = formatTableName(table, this.database);
|
|
305
383
|
const indexName = quoteIdentifier(name, "index name");
|
|
306
384
|
try {
|
|
307
|
-
const
|
|
308
|
-
|
|
385
|
+
const snapshot = this.schemaSnapshot;
|
|
386
|
+
if (snapshot) {
|
|
387
|
+
if (snapshot.indexes.has(indexKey(table, name))) return;
|
|
388
|
+
} else {
|
|
389
|
+
const db = await this.getDatabase() ?? "";
|
|
390
|
+
const [existing] = await this.pool.execute(`SELECT 1 FROM information_schema.STATISTICS
|
|
309
391
|
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND INDEX_NAME = ?
|
|
310
392
|
LIMIT 1`, [
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
393
|
+
db,
|
|
394
|
+
table,
|
|
395
|
+
name
|
|
396
|
+
]);
|
|
397
|
+
if (existing.length > 0) return;
|
|
398
|
+
}
|
|
316
399
|
const [columnMeta] = await this.pool.execute(`SELECT COLUMN_NAME, DATA_TYPE FROM information_schema.COLUMNS
|
|
317
|
-
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?`, [
|
|
400
|
+
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?`, [await this.getDatabase() ?? "", table]);
|
|
318
401
|
const dataTypeByColumn = new Map(columnMeta.map((row) => [String(row.COLUMN_NAME).toLowerCase(), String(row.DATA_TYPE).toLowerCase()]));
|
|
319
402
|
const PREFIX_TYPES = /* @__PURE__ */ new Set([
|
|
320
403
|
"tinytext",
|
|
@@ -340,6 +423,7 @@ var StoreOperationsMySQL = class extends StoreOperations {
|
|
|
340
423
|
}).join(", ");
|
|
341
424
|
const sql = `CREATE ${unique ? "UNIQUE " : ""}INDEX ${indexName} ON ${tableName} (${columnsStr})`;
|
|
342
425
|
await this.pool.execute(sql);
|
|
426
|
+
this.schemaSnapshot?.indexes.add(indexKey(table, name));
|
|
343
427
|
} catch (error) {
|
|
344
428
|
console.warn(`Failed to create index ${name}:`, error);
|
|
345
429
|
}
|
|
@@ -571,17 +655,22 @@ var StoreOperationsMySQL = class extends StoreOperations {
|
|
|
571
655
|
}
|
|
572
656
|
async alterTable({ tableName, schema, ifNotExists }) {
|
|
573
657
|
if (!ifNotExists.length) return;
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
if (
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
params
|
|
658
|
+
const snapshotColumns = this.schemaSnapshot?.columns.get(tableName.toLowerCase());
|
|
659
|
+
let existing;
|
|
660
|
+
if (snapshotColumns) existing = snapshotColumns;
|
|
661
|
+
else {
|
|
662
|
+
const db = await this.getDatabase();
|
|
663
|
+
const [tableRows] = await this.pool.execute("SELECT COUNT(*) AS count FROM information_schema.tables WHERE table_schema = ? AND table_name = ?", [db ?? "", tableName]);
|
|
664
|
+
if (!(Array.isArray(tableRows) && tableRows.length > 0 && tableRows[0].count > 0)) return;
|
|
665
|
+
const params = [tableName];
|
|
666
|
+
let sql = "SELECT column_name FROM information_schema.columns WHERE table_name = ?";
|
|
667
|
+
if (db) {
|
|
668
|
+
sql += " AND table_schema = ?";
|
|
669
|
+
params.push(db);
|
|
670
|
+
}
|
|
671
|
+
const [rows] = await this.pool.execute(sql, params);
|
|
672
|
+
existing = new Set((rows || []).map((row) => String(row.column_name ?? row.COLUMN_NAME).toLowerCase()));
|
|
582
673
|
}
|
|
583
|
-
const [rows] = await this.pool.execute(sql, params);
|
|
584
|
-
const existing = new Set((rows || []).map((row) => String(row.column_name ?? row.COLUMN_NAME).toLowerCase()));
|
|
585
674
|
for (const columnName of ifNotExists) {
|
|
586
675
|
if (existing.has(columnName.toLowerCase())) continue;
|
|
587
676
|
const column = schema[columnName];
|
|
@@ -603,6 +692,7 @@ var StoreOperationsMySQL = class extends StoreOperations {
|
|
|
603
692
|
const alterSql = `ALTER TABLE ${formatTableName(tableName, this.database)} ADD COLUMN ${parts.join(" ")}`;
|
|
604
693
|
try {
|
|
605
694
|
await this.pool.execute(alterSql);
|
|
695
|
+
existing.add(columnName.toLowerCase());
|
|
606
696
|
} catch (error) {
|
|
607
697
|
if (error?.code === "ER_DUP_FIELDNAME") continue;
|
|
608
698
|
throw new MastraError({
|
|
@@ -759,7 +849,8 @@ var AgentsMySQL = class AgentsMySQL extends AgentsStorage {
|
|
|
759
849
|
"requestContextSchema",
|
|
760
850
|
"workspace",
|
|
761
851
|
"skills",
|
|
762
|
-
"skillsFormat"
|
|
852
|
+
"skillsFormat",
|
|
853
|
+
"durable"
|
|
763
854
|
]
|
|
764
855
|
});
|
|
765
856
|
await this.createDefaultIndexes();
|
|
@@ -1051,6 +1142,7 @@ var AgentsMySQL = class AgentsMySQL extends AgentsStorage {
|
|
|
1051
1142
|
workspace: input.workspace ?? null,
|
|
1052
1143
|
skills: input.skills ?? null,
|
|
1053
1144
|
skillsFormat: input.skillsFormat ?? null,
|
|
1145
|
+
durable: input.durable ?? null,
|
|
1054
1146
|
changedFields: input.changedFields ?? null,
|
|
1055
1147
|
changeMessage: input.changeMessage ?? null,
|
|
1056
1148
|
createdAt: now
|
|
@@ -1266,6 +1358,7 @@ var AgentsMySQL = class AgentsMySQL extends AgentsStorage {
|
|
|
1266
1358
|
workspace: this.safeParseJSON(row.workspace),
|
|
1267
1359
|
skills: this.safeParseJSON(row.skills),
|
|
1268
1360
|
skillsFormat: row.skillsFormat,
|
|
1361
|
+
durable: this.safeParseJSON(row.durable),
|
|
1269
1362
|
changedFields: this.safeParseJSON(row.changedFields),
|
|
1270
1363
|
changeMessage: row.changeMessage ?? void 0,
|
|
1271
1364
|
createdAt: row.createdAt instanceof Date ? row.createdAt : new Date(row.createdAt)
|
|
@@ -4832,10 +4925,14 @@ var MemoryMySQL = class MemoryMySQL extends MemoryStorage {
|
|
|
4832
4925
|
schema: TABLE_SCHEMAS[TABLE_MESSAGES],
|
|
4833
4926
|
ifNotExists: ["resourceId"]
|
|
4834
4927
|
});
|
|
4835
|
-
if (omSchema)
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4928
|
+
if (omSchema) {
|
|
4929
|
+
const snapshot = this.operations.getInitSchemaSnapshot();
|
|
4930
|
+
if (!snapshot?.indexes.has(indexKey(OM_TABLE, "idx_om_lookup_key"))) try {
|
|
4931
|
+
await this.pool.execute(`CREATE INDEX idx_om_lookup_key ON ${OM_TABLE_QUOTED} (${quoteIdentifier("lookupKey", "column name")}(191))`);
|
|
4932
|
+
snapshot?.indexes.add(indexKey(OM_TABLE, "idx_om_lookup_key"));
|
|
4933
|
+
} catch (err) {
|
|
4934
|
+
if (err?.errno !== 1061) throw err;
|
|
4935
|
+
}
|
|
4839
4936
|
}
|
|
4840
4937
|
await this.createDefaultIndexes();
|
|
4841
4938
|
await this.createCustomIndexes();
|
|
@@ -10354,6 +10451,7 @@ function parseConnectionString(connectionString, overrides) {
|
|
|
10354
10451
|
}
|
|
10355
10452
|
var MySQLStore = class extends MastraCompositeStore {
|
|
10356
10453
|
pool;
|
|
10454
|
+
operations;
|
|
10357
10455
|
stores;
|
|
10358
10456
|
constructor(config) {
|
|
10359
10457
|
super({
|
|
@@ -10368,6 +10466,7 @@ var MySQLStore = class extends MastraCompositeStore {
|
|
|
10368
10466
|
pool: this.pool,
|
|
10369
10467
|
database
|
|
10370
10468
|
});
|
|
10469
|
+
this.operations = operations;
|
|
10371
10470
|
const memory = new MemoryMySQL({
|
|
10372
10471
|
pool: this.pool,
|
|
10373
10472
|
operations,
|
|
@@ -10510,6 +10609,7 @@ var MySQLStore = class extends MastraCompositeStore {
|
|
|
10510
10609
|
async init() {
|
|
10511
10610
|
try {
|
|
10512
10611
|
(await this.pool.getConnection()).release();
|
|
10612
|
+
await this.operations.loadInitSchemaSnapshot();
|
|
10513
10613
|
await super.init();
|
|
10514
10614
|
} catch (error) {
|
|
10515
10615
|
throw new MastraError({
|
|
@@ -10517,6 +10617,8 @@ var MySQLStore = class extends MastraCompositeStore {
|
|
|
10517
10617
|
domain: ErrorDomain.STORAGE,
|
|
10518
10618
|
category: ErrorCategory.THIRD_PARTY
|
|
10519
10619
|
}, error);
|
|
10620
|
+
} finally {
|
|
10621
|
+
this.operations.clearInitSchemaSnapshot();
|
|
10520
10622
|
}
|
|
10521
10623
|
}
|
|
10522
10624
|
async close() {
|