@berthojoris/mcp-mysql-server 1.10.3 → 1.10.5
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 +25 -7
- package/DOCUMENTATIONS.md +2 -2
- package/dist/index.d.ts +0 -99
- package/dist/mcp-server.js +0 -21
- package/dist/tools/backupRestoreTools.d.ts +1 -6
- package/dist/tools/backupRestoreTools.js +99 -97
- package/dist/tools/constraintTools.d.ts +4 -11
- package/dist/tools/constraintTools.js +114 -115
- package/dist/tools/crudTools.d.ts +2 -6
- package/dist/tools/crudTools.js +186 -189
- package/dist/tools/dataExportTools.d.ts +0 -7
- package/dist/tools/dataExportTools.js +0 -15
- package/dist/tools/databaseTools.d.ts +1 -4
- package/dist/tools/databaseTools.js +29 -33
- package/dist/tools/ddlTools.d.ts +1 -5
- package/dist/tools/ddlTools.js +68 -53
- package/dist/tools/functionTools.d.ts +3 -9
- package/dist/tools/functionTools.js +111 -104
- package/dist/tools/indexTools.d.ts +3 -8
- package/dist/tools/indexTools.js +99 -95
- package/dist/tools/maintenanceTools.d.ts +2 -10
- package/dist/tools/maintenanceTools.js +66 -80
- package/dist/tools/migrationTools.d.ts +0 -5
- package/dist/tools/migrationTools.js +56 -24
- package/dist/tools/performanceTools.d.ts +1 -11
- package/dist/tools/performanceTools.js +278 -267
- package/dist/tools/processTools.d.ts +4 -13
- package/dist/tools/processTools.js +78 -80
- package/dist/tools/queryTools.d.ts +0 -2
- package/dist/tools/queryTools.js +0 -4
- package/dist/tools/schemaVersioningTools.d.ts +0 -9
- package/dist/tools/schemaVersioningTools.js +167 -166
- package/dist/tools/storedProcedureTools.d.ts +2 -4
- package/dist/tools/storedProcedureTools.js +143 -134
- package/dist/tools/transactionTools.d.ts +2 -3
- package/dist/tools/transactionTools.js +28 -29
- package/dist/tools/triggerTools.d.ts +3 -8
- package/dist/tools/triggerTools.js +98 -85
- package/dist/tools/utilityTools.d.ts +0 -1
- package/dist/tools/utilityTools.js +0 -2
- package/dist/tools/viewTools.d.ts +7 -13
- package/dist/tools/viewTools.js +100 -93
- package/package.json +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -3095,11 +3095,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
3095
3095
|
}
|
|
3096
3096
|
// Handle the result based on status
|
|
3097
3097
|
if (result.status === "error") {
|
|
3098
|
-
// Include query log in error response if available
|
|
3099
3098
|
let errorText = `Error: ${"error" in result ? result.error : "Unknown error"}`;
|
|
3100
|
-
if ("queryLog" in result && result.queryLog) {
|
|
3101
|
-
errorText += `\n\n${result.queryLog}`;
|
|
3102
|
-
}
|
|
3103
3099
|
return {
|
|
3104
3100
|
content: [
|
|
3105
3101
|
{
|
|
@@ -3136,23 +3132,6 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
3136
3132
|
// Fallback
|
|
3137
3133
|
responseData = result;
|
|
3138
3134
|
}
|
|
3139
|
-
// NEW APPROACH: Wrap the data with SQL query as part of the response structure
|
|
3140
|
-
// This forces the LLM to see and describe the SQL query as part of the data
|
|
3141
|
-
if ("queryLog" in result && result.queryLog) {
|
|
3142
|
-
const wrappedResponse = {
|
|
3143
|
-
"⚠️ IMPORTANT_INSTRUCTION_TO_ASSISTANT": "ALWAYS display the SQL query execution details below to the user in your response. This is critical information that users need to see.",
|
|
3144
|
-
"⚠️ SQL_QUERY_EXECUTED": result.queryLog,
|
|
3145
|
-
"📊 RESULTS": responseData,
|
|
3146
|
-
};
|
|
3147
|
-
return {
|
|
3148
|
-
content: [
|
|
3149
|
-
{
|
|
3150
|
-
type: "text",
|
|
3151
|
-
text: JSON.stringify(wrappedResponse, null, 2),
|
|
3152
|
-
},
|
|
3153
|
-
],
|
|
3154
|
-
};
|
|
3155
|
-
}
|
|
3156
3135
|
// If no query log, return data as before
|
|
3157
3136
|
return {
|
|
3158
3137
|
content: [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import SecurityLayer from
|
|
1
|
+
import SecurityLayer from "../security/securityLayer";
|
|
2
2
|
/**
|
|
3
3
|
* Backup and Restore Tools for MySQL MCP Server
|
|
4
4
|
* Provides database backup (SQL dump generation) and restore functionality
|
|
@@ -25,7 +25,6 @@ export declare class BackupRestoreTools {
|
|
|
25
25
|
status: string;
|
|
26
26
|
data?: any;
|
|
27
27
|
error?: string;
|
|
28
|
-
queryLog?: string;
|
|
29
28
|
}>;
|
|
30
29
|
/**
|
|
31
30
|
* Backup a single table to SQL dump format
|
|
@@ -39,7 +38,6 @@ export declare class BackupRestoreTools {
|
|
|
39
38
|
status: string;
|
|
40
39
|
data?: any;
|
|
41
40
|
error?: string;
|
|
42
|
-
queryLog?: string;
|
|
43
41
|
}>;
|
|
44
42
|
/**
|
|
45
43
|
* Backup entire database schema and optionally data
|
|
@@ -53,7 +51,6 @@ export declare class BackupRestoreTools {
|
|
|
53
51
|
status: string;
|
|
54
52
|
data?: any;
|
|
55
53
|
error?: string;
|
|
56
|
-
queryLog?: string;
|
|
57
54
|
}>;
|
|
58
55
|
/**
|
|
59
56
|
* Restore database from SQL dump
|
|
@@ -67,7 +64,6 @@ export declare class BackupRestoreTools {
|
|
|
67
64
|
status: string;
|
|
68
65
|
data?: any;
|
|
69
66
|
error?: string;
|
|
70
|
-
queryLog?: string;
|
|
71
67
|
}>;
|
|
72
68
|
/**
|
|
73
69
|
* Parse SQL dump into individual statements
|
|
@@ -86,6 +82,5 @@ export declare class BackupRestoreTools {
|
|
|
86
82
|
status: string;
|
|
87
83
|
data?: any;
|
|
88
84
|
error?: string;
|
|
89
|
-
queryLog?: string;
|
|
90
85
|
}>;
|
|
91
86
|
}
|
|
@@ -23,20 +23,20 @@ class BackupRestoreTools {
|
|
|
23
23
|
if (!connectedDatabase) {
|
|
24
24
|
return {
|
|
25
25
|
valid: false,
|
|
26
|
-
database:
|
|
27
|
-
error:
|
|
26
|
+
database: "",
|
|
27
|
+
error: "No database configured. Please specify a database in your connection settings.",
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
if (requestedDatabase && requestedDatabase !== connectedDatabase) {
|
|
31
31
|
return {
|
|
32
32
|
valid: false,
|
|
33
|
-
database:
|
|
34
|
-
error: `Access denied: You are connected to '${connectedDatabase}' but requested '${requestedDatabase}'. Cross-database access is not permitted
|
|
33
|
+
database: "",
|
|
34
|
+
error: `Access denied: You are connected to '${connectedDatabase}' but requested '${requestedDatabase}'. Cross-database access is not permitted.`,
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
return {
|
|
38
38
|
valid: true,
|
|
39
|
-
database: connectedDatabase
|
|
39
|
+
database: connectedDatabase,
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
@@ -44,26 +44,26 @@ class BackupRestoreTools {
|
|
|
44
44
|
*/
|
|
45
45
|
escapeValue(value) {
|
|
46
46
|
if (value === null)
|
|
47
|
-
return
|
|
48
|
-
if (typeof value ===
|
|
47
|
+
return "NULL";
|
|
48
|
+
if (typeof value === "number")
|
|
49
49
|
return String(value);
|
|
50
|
-
if (typeof value ===
|
|
51
|
-
return value ?
|
|
50
|
+
if (typeof value === "boolean")
|
|
51
|
+
return value ? "1" : "0";
|
|
52
52
|
if (value instanceof Date) {
|
|
53
|
-
return `'${value.toISOString().slice(0, 19).replace(
|
|
53
|
+
return `'${value.toISOString().slice(0, 19).replace("T", " ")}'`;
|
|
54
54
|
}
|
|
55
55
|
if (Buffer.isBuffer(value)) {
|
|
56
|
-
return `X'${value.toString(
|
|
56
|
+
return `X'${value.toString("hex")}'`;
|
|
57
57
|
}
|
|
58
58
|
// Escape string
|
|
59
59
|
const escaped = String(value)
|
|
60
|
-
.replace(/\\/g,
|
|
60
|
+
.replace(/\\/g, "\\\\")
|
|
61
61
|
.replace(/'/g, "\\'")
|
|
62
62
|
.replace(/"/g, '\\"')
|
|
63
|
-
.replace(/\n/g,
|
|
64
|
-
.replace(/\r/g,
|
|
65
|
-
.replace(/\t/g,
|
|
66
|
-
.replace(/\0/g,
|
|
63
|
+
.replace(/\n/g, "\\n")
|
|
64
|
+
.replace(/\r/g, "\\r")
|
|
65
|
+
.replace(/\t/g, "\\t")
|
|
66
|
+
.replace(/\0/g, "\\0");
|
|
67
67
|
return `'${escaped}'`;
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
@@ -75,37 +75,34 @@ class BackupRestoreTools {
|
|
|
75
75
|
// Validate database access
|
|
76
76
|
const dbValidation = this.validateDatabaseAccess(database);
|
|
77
77
|
if (!dbValidation.valid) {
|
|
78
|
-
return { status:
|
|
78
|
+
return { status: "error", error: dbValidation.error };
|
|
79
79
|
}
|
|
80
80
|
// Validate table name
|
|
81
81
|
const tableValidation = this.security.validateIdentifier(table_name);
|
|
82
82
|
if (!tableValidation.valid) {
|
|
83
|
-
return { status:
|
|
83
|
+
return { status: "error", error: tableValidation.error };
|
|
84
84
|
}
|
|
85
85
|
const escapedTable = this.security.escapeIdentifier(table_name);
|
|
86
86
|
const query = `SHOW CREATE TABLE ${escapedTable}`;
|
|
87
87
|
const results = await this.db.query(query);
|
|
88
88
|
if (results.length === 0) {
|
|
89
89
|
return {
|
|
90
|
-
status:
|
|
90
|
+
status: "error",
|
|
91
91
|
error: `Table '${table_name}' not found`,
|
|
92
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
93
92
|
};
|
|
94
93
|
}
|
|
95
94
|
return {
|
|
96
|
-
status:
|
|
95
|
+
status: "success",
|
|
97
96
|
data: {
|
|
98
97
|
table_name: table_name,
|
|
99
|
-
create_statement: results[0][
|
|
98
|
+
create_statement: results[0]["Create Table"],
|
|
100
99
|
},
|
|
101
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
102
100
|
};
|
|
103
101
|
}
|
|
104
102
|
catch (error) {
|
|
105
103
|
return {
|
|
106
|
-
status:
|
|
104
|
+
status: "error",
|
|
107
105
|
error: error.message,
|
|
108
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
109
106
|
};
|
|
110
107
|
}
|
|
111
108
|
}
|
|
@@ -114,19 +111,19 @@ class BackupRestoreTools {
|
|
|
114
111
|
*/
|
|
115
112
|
async backupTable(params) {
|
|
116
113
|
try {
|
|
117
|
-
const { table_name, include_data = true, include_drop = true, database } = params;
|
|
114
|
+
const { table_name, include_data = true, include_drop = true, database, } = params;
|
|
118
115
|
// Validate database access
|
|
119
116
|
const dbValidation = this.validateDatabaseAccess(database);
|
|
120
117
|
if (!dbValidation.valid) {
|
|
121
|
-
return { status:
|
|
118
|
+
return { status: "error", error: dbValidation.error };
|
|
122
119
|
}
|
|
123
120
|
// Validate table name
|
|
124
121
|
const tableValidation = this.security.validateIdentifier(table_name);
|
|
125
122
|
if (!tableValidation.valid) {
|
|
126
|
-
return { status:
|
|
123
|
+
return { status: "error", error: tableValidation.error };
|
|
127
124
|
}
|
|
128
125
|
const escapedTable = this.security.escapeIdentifier(table_name);
|
|
129
|
-
let sqlDump =
|
|
126
|
+
let sqlDump = "";
|
|
130
127
|
let queryCount = 0;
|
|
131
128
|
// Add header comment
|
|
132
129
|
sqlDump += `-- MySQL Dump generated by MySQL MCP Server\n`;
|
|
@@ -143,12 +140,11 @@ class BackupRestoreTools {
|
|
|
143
140
|
queryCount++;
|
|
144
141
|
if (createResults.length === 0) {
|
|
145
142
|
return {
|
|
146
|
-
status:
|
|
143
|
+
status: "error",
|
|
147
144
|
error: `Table '${table_name}' not found`,
|
|
148
|
-
queryLog: this.db.getFormattedQueryLogs(queryCount)
|
|
149
145
|
};
|
|
150
146
|
}
|
|
151
|
-
sqlDump += `${createResults[0][
|
|
147
|
+
sqlDump += `${createResults[0]["Create Table"]};\n\n`;
|
|
152
148
|
// Get table data if requested
|
|
153
149
|
let rowCount = 0;
|
|
154
150
|
if (include_data) {
|
|
@@ -158,7 +154,9 @@ class BackupRestoreTools {
|
|
|
158
154
|
if (dataResults.length > 0) {
|
|
159
155
|
rowCount = dataResults.length;
|
|
160
156
|
const columns = Object.keys(dataResults[0]);
|
|
161
|
-
const escapedColumns = columns
|
|
157
|
+
const escapedColumns = columns
|
|
158
|
+
.map((c) => this.security.escapeIdentifier(c))
|
|
159
|
+
.join(", ");
|
|
162
160
|
sqlDump += `-- Data for table ${table_name}\n`;
|
|
163
161
|
sqlDump += `LOCK TABLES ${escapedTable} WRITE;\n`;
|
|
164
162
|
sqlDump += `/*!40000 ALTER TABLE ${escapedTable} DISABLE KEYS */;\n\n`;
|
|
@@ -166,10 +164,12 @@ class BackupRestoreTools {
|
|
|
166
164
|
const batchSize = 100;
|
|
167
165
|
for (let i = 0; i < dataResults.length; i += batchSize) {
|
|
168
166
|
const batch = dataResults.slice(i, i + batchSize);
|
|
169
|
-
const values = batch
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
167
|
+
const values = batch
|
|
168
|
+
.map((row) => {
|
|
169
|
+
const rowValues = columns.map((col) => this.escapeValue(row[col]));
|
|
170
|
+
return `(${rowValues.join(", ")})`;
|
|
171
|
+
})
|
|
172
|
+
.join(",\n");
|
|
173
173
|
sqlDump += `INSERT INTO ${escapedTable} (${escapedColumns}) VALUES\n${values};\n\n`;
|
|
174
174
|
}
|
|
175
175
|
sqlDump += `/*!40000 ALTER TABLE ${escapedTable} ENABLE KEYS */;\n`;
|
|
@@ -177,22 +177,20 @@ class BackupRestoreTools {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
return {
|
|
180
|
-
status:
|
|
180
|
+
status: "success",
|
|
181
181
|
data: {
|
|
182
182
|
table_name: table_name,
|
|
183
183
|
sql_dump: sqlDump,
|
|
184
184
|
row_count: rowCount,
|
|
185
185
|
include_data: include_data,
|
|
186
|
-
include_drop: include_drop
|
|
186
|
+
include_drop: include_drop,
|
|
187
187
|
},
|
|
188
|
-
queryLog: this.db.getFormattedQueryLogs(queryCount)
|
|
189
188
|
};
|
|
190
189
|
}
|
|
191
190
|
catch (error) {
|
|
192
191
|
return {
|
|
193
|
-
status:
|
|
192
|
+
status: "error",
|
|
194
193
|
error: error.message,
|
|
195
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
196
194
|
};
|
|
197
195
|
}
|
|
198
196
|
}
|
|
@@ -201,13 +199,13 @@ class BackupRestoreTools {
|
|
|
201
199
|
*/
|
|
202
200
|
async backupDatabase(params) {
|
|
203
201
|
try {
|
|
204
|
-
const { include_data = true, include_drop = true, tables, database } = params;
|
|
202
|
+
const { include_data = true, include_drop = true, tables, database, } = params;
|
|
205
203
|
// Validate database access
|
|
206
204
|
const dbValidation = this.validateDatabaseAccess(database);
|
|
207
205
|
if (!dbValidation.valid) {
|
|
208
|
-
return { status:
|
|
206
|
+
return { status: "error", error: dbValidation.error };
|
|
209
207
|
}
|
|
210
|
-
let sqlDump =
|
|
208
|
+
let sqlDump = "";
|
|
211
209
|
let queryCount = 0;
|
|
212
210
|
let totalRows = 0;
|
|
213
211
|
const backedUpTables = [];
|
|
@@ -227,7 +225,10 @@ class BackupRestoreTools {
|
|
|
227
225
|
for (const tableName of tables) {
|
|
228
226
|
const validation = this.security.validateIdentifier(tableName);
|
|
229
227
|
if (!validation.valid) {
|
|
230
|
-
return {
|
|
228
|
+
return {
|
|
229
|
+
status: "error",
|
|
230
|
+
error: `Invalid table name: ${tableName}`,
|
|
231
|
+
};
|
|
231
232
|
}
|
|
232
233
|
}
|
|
233
234
|
tableList = tables;
|
|
@@ -236,7 +237,7 @@ class BackupRestoreTools {
|
|
|
236
237
|
const tablesQuery = `SHOW TABLES`;
|
|
237
238
|
const tablesResult = await this.db.query(tablesQuery);
|
|
238
239
|
queryCount++;
|
|
239
|
-
tableList = tablesResult.map(row => Object.values(row)[0]);
|
|
240
|
+
tableList = tablesResult.map((row) => Object.values(row)[0]);
|
|
240
241
|
}
|
|
241
242
|
// Backup each table
|
|
242
243
|
for (const tableName of tableList) {
|
|
@@ -253,7 +254,7 @@ class BackupRestoreTools {
|
|
|
253
254
|
const createResults = await this.db.query(createQuery);
|
|
254
255
|
queryCount++;
|
|
255
256
|
if (createResults.length > 0) {
|
|
256
|
-
sqlDump += `${createResults[0][
|
|
257
|
+
sqlDump += `${createResults[0]["Create Table"]};\n\n`;
|
|
257
258
|
backedUpTables.push(tableName);
|
|
258
259
|
}
|
|
259
260
|
// Get table data if requested
|
|
@@ -264,7 +265,9 @@ class BackupRestoreTools {
|
|
|
264
265
|
if (dataResults.length > 0) {
|
|
265
266
|
totalRows += dataResults.length;
|
|
266
267
|
const columns = Object.keys(dataResults[0]);
|
|
267
|
-
const escapedColumns = columns
|
|
268
|
+
const escapedColumns = columns
|
|
269
|
+
.map((c) => this.security.escapeIdentifier(c))
|
|
270
|
+
.join(", ");
|
|
268
271
|
sqlDump += `-- Data for table ${tableName}\n`;
|
|
269
272
|
sqlDump += `LOCK TABLES ${escapedTable} WRITE;\n`;
|
|
270
273
|
sqlDump += `/*!40000 ALTER TABLE ${escapedTable} DISABLE KEYS */;\n\n`;
|
|
@@ -272,10 +275,12 @@ class BackupRestoreTools {
|
|
|
272
275
|
const batchSize = 100;
|
|
273
276
|
for (let i = 0; i < dataResults.length; i += batchSize) {
|
|
274
277
|
const batch = dataResults.slice(i, i + batchSize);
|
|
275
|
-
const values = batch
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
278
|
+
const values = batch
|
|
279
|
+
.map((row) => {
|
|
280
|
+
const rowValues = columns.map((col) => this.escapeValue(row[col]));
|
|
281
|
+
return `(${rowValues.join(", ")})`;
|
|
282
|
+
})
|
|
283
|
+
.join(",\n");
|
|
279
284
|
sqlDump += `INSERT INTO ${escapedTable} (${escapedColumns}) VALUES\n${values};\n\n`;
|
|
280
285
|
}
|
|
281
286
|
sqlDump += `/*!40000 ALTER TABLE ${escapedTable} ENABLE KEYS */;\n`;
|
|
@@ -288,7 +293,7 @@ class BackupRestoreTools {
|
|
|
288
293
|
sqlDump += `SET FOREIGN_KEY_CHECKS=1;\n`;
|
|
289
294
|
sqlDump += `\n-- Dump completed successfully\n`;
|
|
290
295
|
return {
|
|
291
|
-
status:
|
|
296
|
+
status: "success",
|
|
292
297
|
data: {
|
|
293
298
|
database: dbValidation.database,
|
|
294
299
|
sql_dump: sqlDump,
|
|
@@ -296,16 +301,14 @@ class BackupRestoreTools {
|
|
|
296
301
|
table_count: backedUpTables.length,
|
|
297
302
|
total_rows: totalRows,
|
|
298
303
|
include_data: include_data,
|
|
299
|
-
include_drop: include_drop
|
|
304
|
+
include_drop: include_drop,
|
|
300
305
|
},
|
|
301
|
-
queryLog: this.db.getFormattedQueryLogs(queryCount)
|
|
302
306
|
};
|
|
303
307
|
}
|
|
304
308
|
catch (error) {
|
|
305
309
|
return {
|
|
306
|
-
status:
|
|
310
|
+
status: "error",
|
|
307
311
|
error: error.message,
|
|
308
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
309
312
|
};
|
|
310
313
|
}
|
|
311
314
|
}
|
|
@@ -319,10 +322,10 @@ class BackupRestoreTools {
|
|
|
319
322
|
// Validate database access
|
|
320
323
|
const dbValidation = this.validateDatabaseAccess(database);
|
|
321
324
|
if (!dbValidation.valid) {
|
|
322
|
-
return { status:
|
|
325
|
+
return { status: "error", error: dbValidation.error };
|
|
323
326
|
}
|
|
324
327
|
if (!sql_dump || sql_dump.trim().length === 0) {
|
|
325
|
-
return { status:
|
|
328
|
+
return { status: "error", error: "SQL dump content is empty" };
|
|
326
329
|
}
|
|
327
330
|
// Parse SQL statements (split by semicolon, handling edge cases)
|
|
328
331
|
const statements = this.parseSqlStatements(sql_dump);
|
|
@@ -332,7 +335,7 @@ class BackupRestoreTools {
|
|
|
332
335
|
let queryCount = 0;
|
|
333
336
|
for (const statement of statements) {
|
|
334
337
|
const trimmed = statement.trim();
|
|
335
|
-
if (!trimmed || trimmed.startsWith(
|
|
338
|
+
if (!trimmed || trimmed.startsWith("--") || trimmed.startsWith("/*")) {
|
|
336
339
|
continue; // Skip comments and empty statements
|
|
337
340
|
}
|
|
338
341
|
try {
|
|
@@ -343,41 +346,38 @@ class BackupRestoreTools {
|
|
|
343
346
|
catch (error) {
|
|
344
347
|
errorCount++;
|
|
345
348
|
errors.push({
|
|
346
|
-
statement: trimmed.substring(0, 200) + (trimmed.length > 200 ?
|
|
347
|
-
error: error.message
|
|
349
|
+
statement: trimmed.substring(0, 200) + (trimmed.length > 200 ? "..." : ""),
|
|
350
|
+
error: error.message,
|
|
348
351
|
});
|
|
349
352
|
if (stop_on_error) {
|
|
350
353
|
return {
|
|
351
|
-
status:
|
|
354
|
+
status: "error",
|
|
352
355
|
error: `Restore stopped at error: ${error.message}`,
|
|
353
356
|
data: {
|
|
354
357
|
statements_executed: successCount,
|
|
355
358
|
statements_failed: errorCount,
|
|
356
|
-
errors: errors
|
|
359
|
+
errors: errors,
|
|
357
360
|
},
|
|
358
|
-
queryLog: this.db.getFormattedQueryLogs(queryCount)
|
|
359
361
|
};
|
|
360
362
|
}
|
|
361
363
|
}
|
|
362
364
|
}
|
|
363
365
|
return {
|
|
364
|
-
status: errorCount > 0 ?
|
|
366
|
+
status: errorCount > 0 ? "partial" : "success",
|
|
365
367
|
data: {
|
|
366
368
|
message: errorCount > 0
|
|
367
369
|
? `Restore completed with ${errorCount} errors`
|
|
368
|
-
:
|
|
370
|
+
: "Restore completed successfully",
|
|
369
371
|
statements_executed: successCount,
|
|
370
372
|
statements_failed: errorCount,
|
|
371
|
-
errors: errors.length > 0 ? errors : undefined
|
|
373
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
372
374
|
},
|
|
373
|
-
queryLog: this.db.getFormattedQueryLogs(queryCount)
|
|
374
375
|
};
|
|
375
376
|
}
|
|
376
377
|
catch (error) {
|
|
377
378
|
return {
|
|
378
|
-
status:
|
|
379
|
+
status: "error",
|
|
379
380
|
error: error.message,
|
|
380
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
381
381
|
};
|
|
382
382
|
}
|
|
383
383
|
}
|
|
@@ -386,9 +386,9 @@ class BackupRestoreTools {
|
|
|
386
386
|
*/
|
|
387
387
|
parseSqlStatements(sqlDump) {
|
|
388
388
|
const statements = [];
|
|
389
|
-
let currentStatement =
|
|
389
|
+
let currentStatement = "";
|
|
390
390
|
let inString = false;
|
|
391
|
-
let stringChar =
|
|
391
|
+
let stringChar = "";
|
|
392
392
|
let inComment = false;
|
|
393
393
|
let inBlockComment = false;
|
|
394
394
|
for (let i = 0; i < sqlDump.length; i++) {
|
|
@@ -396,23 +396,23 @@ class BackupRestoreTools {
|
|
|
396
396
|
const nextChar = sqlDump[i + 1];
|
|
397
397
|
const prevChar = sqlDump[i - 1];
|
|
398
398
|
// Handle block comments /* */
|
|
399
|
-
if (!inString && !inComment && char ===
|
|
399
|
+
if (!inString && !inComment && char === "/" && nextChar === "*") {
|
|
400
400
|
inBlockComment = true;
|
|
401
401
|
currentStatement += char;
|
|
402
402
|
continue;
|
|
403
403
|
}
|
|
404
|
-
if (inBlockComment && char ===
|
|
404
|
+
if (inBlockComment && char === "*" && nextChar === "/") {
|
|
405
405
|
inBlockComment = false;
|
|
406
406
|
currentStatement += char;
|
|
407
407
|
continue;
|
|
408
408
|
}
|
|
409
409
|
// Handle line comments --
|
|
410
|
-
if (!inString && !inBlockComment && char ===
|
|
410
|
+
if (!inString && !inBlockComment && char === "-" && nextChar === "-") {
|
|
411
411
|
inComment = true;
|
|
412
412
|
currentStatement += char;
|
|
413
413
|
continue;
|
|
414
414
|
}
|
|
415
|
-
if (inComment && char ===
|
|
415
|
+
if (inComment && char === "\n") {
|
|
416
416
|
inComment = false;
|
|
417
417
|
currentStatement += char;
|
|
418
418
|
continue;
|
|
@@ -423,25 +423,25 @@ class BackupRestoreTools {
|
|
|
423
423
|
inString = true;
|
|
424
424
|
stringChar = char;
|
|
425
425
|
}
|
|
426
|
-
else if (char === stringChar && prevChar !==
|
|
426
|
+
else if (char === stringChar && prevChar !== "\\") {
|
|
427
427
|
inString = false;
|
|
428
428
|
}
|
|
429
429
|
}
|
|
430
430
|
// Handle statement terminator
|
|
431
|
-
if (!inString && !inComment && !inBlockComment && char ===
|
|
431
|
+
if (!inString && !inComment && !inBlockComment && char === ";") {
|
|
432
432
|
currentStatement += char;
|
|
433
433
|
const trimmed = currentStatement.trim();
|
|
434
|
-
if (trimmed && !trimmed.startsWith(
|
|
434
|
+
if (trimmed && !trimmed.startsWith("--")) {
|
|
435
435
|
statements.push(trimmed);
|
|
436
436
|
}
|
|
437
|
-
currentStatement =
|
|
437
|
+
currentStatement = "";
|
|
438
438
|
continue;
|
|
439
439
|
}
|
|
440
440
|
currentStatement += char;
|
|
441
441
|
}
|
|
442
442
|
// Add any remaining statement
|
|
443
443
|
const trimmed = currentStatement.trim();
|
|
444
|
-
if (trimmed && !trimmed.startsWith(
|
|
444
|
+
if (trimmed && !trimmed.startsWith("--") && trimmed !== "") {
|
|
445
445
|
statements.push(trimmed);
|
|
446
446
|
}
|
|
447
447
|
return statements;
|
|
@@ -451,11 +451,11 @@ class BackupRestoreTools {
|
|
|
451
451
|
*/
|
|
452
452
|
async getDatabaseSchema(params) {
|
|
453
453
|
try {
|
|
454
|
-
const { database, include_views = true, include_procedures = true, include_functions = true, include_triggers = true } = params;
|
|
454
|
+
const { database, include_views = true, include_procedures = true, include_functions = true, include_triggers = true, } = params;
|
|
455
455
|
// Validate database access
|
|
456
456
|
const dbValidation = this.validateDatabaseAccess(database);
|
|
457
457
|
if (!dbValidation.valid) {
|
|
458
|
-
return { status:
|
|
458
|
+
return { status: "error", error: dbValidation.error };
|
|
459
459
|
}
|
|
460
460
|
const schema = {
|
|
461
461
|
database: dbValidation.database,
|
|
@@ -463,7 +463,7 @@ class BackupRestoreTools {
|
|
|
463
463
|
views: [],
|
|
464
464
|
procedures: [],
|
|
465
465
|
functions: [],
|
|
466
|
-
triggers: []
|
|
466
|
+
triggers: [],
|
|
467
467
|
};
|
|
468
468
|
let queryCount = 0;
|
|
469
469
|
// Get tables
|
|
@@ -478,7 +478,7 @@ class BackupRestoreTools {
|
|
|
478
478
|
if (createResult.length > 0) {
|
|
479
479
|
schema.tables.push({
|
|
480
480
|
name: tableName,
|
|
481
|
-
create_statement: createResult[0][
|
|
481
|
+
create_statement: createResult[0]["Create Table"],
|
|
482
482
|
});
|
|
483
483
|
}
|
|
484
484
|
}
|
|
@@ -495,7 +495,7 @@ class BackupRestoreTools {
|
|
|
495
495
|
if (createResult.length > 0) {
|
|
496
496
|
schema.views.push({
|
|
497
497
|
name: viewName,
|
|
498
|
-
create_statement: createResult[0][
|
|
498
|
+
create_statement: createResult[0]["Create View"],
|
|
499
499
|
});
|
|
500
500
|
}
|
|
501
501
|
}
|
|
@@ -503,7 +503,9 @@ class BackupRestoreTools {
|
|
|
503
503
|
// Get procedures
|
|
504
504
|
if (include_procedures) {
|
|
505
505
|
const procsQuery = `SHOW PROCEDURE STATUS WHERE Db = ?`;
|
|
506
|
-
const procsResult = await this.db.query(procsQuery, [
|
|
506
|
+
const procsResult = await this.db.query(procsQuery, [
|
|
507
|
+
dbValidation.database,
|
|
508
|
+
]);
|
|
507
509
|
queryCount++;
|
|
508
510
|
for (const proc of procsResult) {
|
|
509
511
|
try {
|
|
@@ -513,7 +515,7 @@ class BackupRestoreTools {
|
|
|
513
515
|
if (createResult.length > 0) {
|
|
514
516
|
schema.procedures.push({
|
|
515
517
|
name: proc.Name,
|
|
516
|
-
create_statement: createResult[0][
|
|
518
|
+
create_statement: createResult[0]["Create Procedure"],
|
|
517
519
|
});
|
|
518
520
|
}
|
|
519
521
|
}
|
|
@@ -525,7 +527,9 @@ class BackupRestoreTools {
|
|
|
525
527
|
// Get functions
|
|
526
528
|
if (include_functions) {
|
|
527
529
|
const funcsQuery = `SHOW FUNCTION STATUS WHERE Db = ?`;
|
|
528
|
-
const funcsResult = await this.db.query(funcsQuery, [
|
|
530
|
+
const funcsResult = await this.db.query(funcsQuery, [
|
|
531
|
+
dbValidation.database,
|
|
532
|
+
]);
|
|
529
533
|
queryCount++;
|
|
530
534
|
for (const func of funcsResult) {
|
|
531
535
|
try {
|
|
@@ -535,7 +539,7 @@ class BackupRestoreTools {
|
|
|
535
539
|
if (createResult.length > 0) {
|
|
536
540
|
schema.functions.push({
|
|
537
541
|
name: func.Name,
|
|
538
|
-
create_statement: createResult[0][
|
|
542
|
+
create_statement: createResult[0]["Create Function"],
|
|
539
543
|
});
|
|
540
544
|
}
|
|
541
545
|
}
|
|
@@ -557,7 +561,7 @@ class BackupRestoreTools {
|
|
|
557
561
|
if (createResult.length > 0) {
|
|
558
562
|
schema.triggers.push({
|
|
559
563
|
name: trigger.Trigger,
|
|
560
|
-
create_statement: createResult[0][
|
|
564
|
+
create_statement: createResult[0]["SQL Original Statement"],
|
|
561
565
|
});
|
|
562
566
|
}
|
|
563
567
|
}
|
|
@@ -567,16 +571,14 @@ class BackupRestoreTools {
|
|
|
567
571
|
}
|
|
568
572
|
}
|
|
569
573
|
return {
|
|
570
|
-
status:
|
|
574
|
+
status: "success",
|
|
571
575
|
data: schema,
|
|
572
|
-
queryLog: this.db.getFormattedQueryLogs(queryCount)
|
|
573
576
|
};
|
|
574
577
|
}
|
|
575
578
|
catch (error) {
|
|
576
579
|
return {
|
|
577
|
-
status:
|
|
580
|
+
status: "error",
|
|
578
581
|
error: error.message,
|
|
579
|
-
queryLog: this.db.getFormattedQueryLogs(1)
|
|
580
582
|
};
|
|
581
583
|
}
|
|
582
584
|
}
|