@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.
Files changed (43) hide show
  1. package/CHANGELOG.md +25 -7
  2. package/DOCUMENTATIONS.md +2 -2
  3. package/dist/index.d.ts +0 -99
  4. package/dist/mcp-server.js +0 -21
  5. package/dist/tools/backupRestoreTools.d.ts +1 -6
  6. package/dist/tools/backupRestoreTools.js +99 -97
  7. package/dist/tools/constraintTools.d.ts +4 -11
  8. package/dist/tools/constraintTools.js +114 -115
  9. package/dist/tools/crudTools.d.ts +2 -6
  10. package/dist/tools/crudTools.js +186 -189
  11. package/dist/tools/dataExportTools.d.ts +0 -7
  12. package/dist/tools/dataExportTools.js +0 -15
  13. package/dist/tools/databaseTools.d.ts +1 -4
  14. package/dist/tools/databaseTools.js +29 -33
  15. package/dist/tools/ddlTools.d.ts +1 -5
  16. package/dist/tools/ddlTools.js +68 -53
  17. package/dist/tools/functionTools.d.ts +3 -9
  18. package/dist/tools/functionTools.js +111 -104
  19. package/dist/tools/indexTools.d.ts +3 -8
  20. package/dist/tools/indexTools.js +99 -95
  21. package/dist/tools/maintenanceTools.d.ts +2 -10
  22. package/dist/tools/maintenanceTools.js +66 -80
  23. package/dist/tools/migrationTools.d.ts +0 -5
  24. package/dist/tools/migrationTools.js +56 -24
  25. package/dist/tools/performanceTools.d.ts +1 -11
  26. package/dist/tools/performanceTools.js +278 -267
  27. package/dist/tools/processTools.d.ts +4 -13
  28. package/dist/tools/processTools.js +78 -80
  29. package/dist/tools/queryTools.d.ts +0 -2
  30. package/dist/tools/queryTools.js +0 -4
  31. package/dist/tools/schemaVersioningTools.d.ts +0 -9
  32. package/dist/tools/schemaVersioningTools.js +167 -166
  33. package/dist/tools/storedProcedureTools.d.ts +2 -4
  34. package/dist/tools/storedProcedureTools.js +143 -134
  35. package/dist/tools/transactionTools.d.ts +2 -3
  36. package/dist/tools/transactionTools.js +28 -29
  37. package/dist/tools/triggerTools.d.ts +3 -8
  38. package/dist/tools/triggerTools.js +98 -85
  39. package/dist/tools/utilityTools.d.ts +0 -1
  40. package/dist/tools/utilityTools.js +0 -2
  41. package/dist/tools/viewTools.d.ts +7 -13
  42. package/dist/tools/viewTools.js +100 -93
  43. package/package.json +1 -1
@@ -17,28 +17,28 @@ class PerformanceTools {
17
17
  try {
18
18
  const metrics = {};
19
19
  // Get query performance metrics
20
- const perfQuery = `
21
- SELECT
22
- SUM(TIMER_WAIT) / 1000000000000 as total_execution_time_sec,
23
- SUM(LOCK_TIME) / 1000000000000 as total_lock_time_sec,
24
- SUM(ROWS_EXAMINED) as total_rows_examined,
25
- SUM(ROWS_SENT) as total_rows_sent,
26
- SUM(ROWS_AFFECTED) as total_rows_affected,
27
- SUM(SELECT_FULL_JOIN) as full_table_scans,
28
- SUM(NO_INDEX_USED) as queries_without_indexes,
29
- SUM(NO_GOOD_INDEX_USED) as queries_with_bad_indexes,
30
- COUNT(*) as total_queries
31
- FROM performance_schema.events_statements_summary_global_by_event_name
32
- WHERE EVENT_NAME LIKE 'statement/sql/%'
20
+ const perfQuery = `
21
+ SELECT
22
+ SUM(TIMER_WAIT) / 1000000000000 as total_execution_time_sec,
23
+ SUM(LOCK_TIME) / 1000000000000 as total_lock_time_sec,
24
+ SUM(ROWS_EXAMINED) as total_rows_examined,
25
+ SUM(ROWS_SENT) as total_rows_sent,
26
+ SUM(ROWS_AFFECTED) as total_rows_affected,
27
+ SUM(SELECT_FULL_JOIN) as full_table_scans,
28
+ SUM(NO_INDEX_USED) as queries_without_indexes,
29
+ SUM(NO_GOOD_INDEX_USED) as queries_with_bad_indexes,
30
+ COUNT(*) as total_queries
31
+ FROM performance_schema.events_statements_summary_global_by_event_name
32
+ WHERE EVENT_NAME LIKE 'statement/sql/%'
33
33
  `;
34
34
  const perfResult = await this.db.query(perfQuery);
35
35
  metrics.query_performance = perfResult[0] || {};
36
36
  // Get connection metrics
37
- const connQuery = `
38
- SHOW GLOBAL STATUS WHERE Variable_name IN (
39
- 'Threads_connected', 'Threads_running', 'Threads_created',
40
- 'Connections', 'Max_used_connections', 'Aborted_connects', 'Aborted_clients'
41
- )
37
+ const connQuery = `
38
+ SHOW GLOBAL STATUS WHERE Variable_name IN (
39
+ 'Threads_connected', 'Threads_running', 'Threads_created',
40
+ 'Connections', 'Max_used_connections', 'Aborted_connects', 'Aborted_clients'
41
+ )
42
42
  `;
43
43
  const connResult = await this.db.query(connQuery);
44
44
  metrics.connections = {};
@@ -46,11 +46,11 @@ class PerformanceTools {
46
46
  metrics.connections[row.Variable_name.toLowerCase()] = row.Value;
47
47
  }
48
48
  // Get table cache metrics
49
- const cacheQuery = `
50
- SHOW GLOBAL STATUS WHERE Variable_name IN (
51
- 'Open_tables', 'Opened_tables', 'Table_open_cache_hits',
52
- 'Table_open_cache_misses', 'Table_open_cache_overflows'
53
- )
49
+ const cacheQuery = `
50
+ SHOW GLOBAL STATUS WHERE Variable_name IN (
51
+ 'Open_tables', 'Opened_tables', 'Table_open_cache_hits',
52
+ 'Table_open_cache_misses', 'Table_open_cache_overflows'
53
+ )
54
54
  `;
55
55
  const cacheResult = await this.db.query(cacheQuery);
56
56
  metrics.table_cache = {};
@@ -58,15 +58,15 @@ class PerformanceTools {
58
58
  metrics.table_cache[row.Variable_name.toLowerCase()] = row.Value;
59
59
  }
60
60
  // Get InnoDB metrics
61
- const innodbQuery = `
62
- SHOW GLOBAL STATUS WHERE Variable_name LIKE 'Innodb_%'
63
- AND Variable_name IN (
64
- 'Innodb_buffer_pool_reads', 'Innodb_buffer_pool_read_requests',
65
- 'Innodb_buffer_pool_pages_total', 'Innodb_buffer_pool_pages_free',
66
- 'Innodb_buffer_pool_pages_data', 'Innodb_buffer_pool_pages_dirty',
67
- 'Innodb_row_lock_waits', 'Innodb_row_lock_time', 'Innodb_rows_read',
68
- 'Innodb_rows_inserted', 'Innodb_rows_updated', 'Innodb_rows_deleted'
69
- )
61
+ const innodbQuery = `
62
+ SHOW GLOBAL STATUS WHERE Variable_name LIKE 'Innodb_%'
63
+ AND Variable_name IN (
64
+ 'Innodb_buffer_pool_reads', 'Innodb_buffer_pool_read_requests',
65
+ 'Innodb_buffer_pool_pages_total', 'Innodb_buffer_pool_pages_free',
66
+ 'Innodb_buffer_pool_pages_data', 'Innodb_buffer_pool_pages_dirty',
67
+ 'Innodb_row_lock_waits', 'Innodb_row_lock_time', 'Innodb_rows_read',
68
+ 'Innodb_rows_inserted', 'Innodb_rows_updated', 'Innodb_rows_deleted'
69
+ )
70
70
  `;
71
71
  const innodbResult = await this.db.query(innodbQuery);
72
72
  metrics.innodb = {};
@@ -74,14 +74,16 @@ class PerformanceTools {
74
74
  metrics.innodb[row.Variable_name.toLowerCase()] = row.Value;
75
75
  }
76
76
  // Calculate buffer pool hit ratio
77
- if (metrics.innodb.innodb_buffer_pool_read_requests && metrics.innodb.innodb_buffer_pool_reads) {
77
+ if (metrics.innodb.innodb_buffer_pool_read_requests &&
78
+ metrics.innodb.innodb_buffer_pool_reads) {
78
79
  const requests = parseFloat(metrics.innodb.innodb_buffer_pool_read_requests);
79
80
  const reads = parseFloat(metrics.innodb.innodb_buffer_pool_reads);
80
- metrics.innodb.buffer_pool_hit_ratio = ((requests - reads) / requests * 100).toFixed(2) + '%';
81
+ metrics.innodb.buffer_pool_hit_ratio =
82
+ (((requests - reads) / requests) * 100).toFixed(2) + "%";
81
83
  }
82
84
  // Get slow query metrics
83
- const slowQuery = `
84
- SHOW GLOBAL STATUS WHERE Variable_name IN ('Slow_queries', 'Questions')
85
+ const slowQuery = `
86
+ SHOW GLOBAL STATUS WHERE Variable_name IN ('Slow_queries', 'Questions')
85
87
  `;
86
88
  const slowResult = await this.db.query(slowQuery);
87
89
  metrics.slow_queries = {};
@@ -92,19 +94,18 @@ class PerformanceTools {
92
94
  if (metrics.slow_queries.questions && metrics.slow_queries.slow_queries) {
93
95
  const total = parseFloat(metrics.slow_queries.questions);
94
96
  const slow = parseFloat(metrics.slow_queries.slow_queries);
95
- metrics.slow_queries.slow_query_percentage = ((slow / total) * 100).toFixed(4) + '%';
97
+ metrics.slow_queries.slow_query_percentage =
98
+ ((slow / total) * 100).toFixed(4) + "%";
96
99
  }
97
100
  return {
98
- status: 'success',
101
+ status: "success",
99
102
  data: metrics,
100
- queryLog: this.db.getFormattedQueryLogs(5)
101
103
  };
102
104
  }
103
105
  catch (error) {
104
106
  return {
105
- status: 'error',
107
+ status: "error",
106
108
  error: error.message,
107
- queryLog: this.db.getFormattedQueryLogs(1)
108
109
  };
109
110
  }
110
111
  }
@@ -115,38 +116,39 @@ class PerformanceTools {
115
116
  try {
116
117
  const limit = params?.limit || 10;
117
118
  if (!Number.isInteger(limit) || limit <= 0 || limit > 100) {
118
- return { status: 'error', error: 'Limit must be a positive integer between 1 and 100' };
119
- }
120
- const query = `
121
- SELECT
122
- DIGEST_TEXT as query_pattern,
123
- COUNT_STAR as execution_count,
124
- ROUND(AVG_TIMER_WAIT / 1000000000000, 6) as avg_execution_time_sec,
125
- ROUND(MAX_TIMER_WAIT / 1000000000000, 6) as max_execution_time_sec,
126
- ROUND(SUM_TIMER_WAIT / 1000000000000, 6) as total_execution_time_sec,
127
- ROUND(SUM_LOCK_TIME / 1000000000000, 6) as total_lock_time_sec,
128
- SUM_ROWS_EXAMINED as rows_examined,
129
- SUM_ROWS_SENT as rows_sent,
130
- SUM_ROWS_AFFECTED as rows_affected,
131
- FIRST_SEEN,
132
- LAST_SEEN
133
- FROM performance_schema.events_statements_summary_by_digest
134
- WHERE DIGEST_TEXT IS NOT NULL
135
- ORDER BY SUM_TIMER_WAIT DESC
136
- LIMIT ${limit}
119
+ return {
120
+ status: "error",
121
+ error: "Limit must be a positive integer between 1 and 100",
122
+ };
123
+ }
124
+ const query = `
125
+ SELECT
126
+ DIGEST_TEXT as query_pattern,
127
+ COUNT_STAR as execution_count,
128
+ ROUND(AVG_TIMER_WAIT / 1000000000000, 6) as avg_execution_time_sec,
129
+ ROUND(MAX_TIMER_WAIT / 1000000000000, 6) as max_execution_time_sec,
130
+ ROUND(SUM_TIMER_WAIT / 1000000000000, 6) as total_execution_time_sec,
131
+ ROUND(SUM_LOCK_TIME / 1000000000000, 6) as total_lock_time_sec,
132
+ SUM_ROWS_EXAMINED as rows_examined,
133
+ SUM_ROWS_SENT as rows_sent,
134
+ SUM_ROWS_AFFECTED as rows_affected,
135
+ FIRST_SEEN,
136
+ LAST_SEEN
137
+ FROM performance_schema.events_statements_summary_by_digest
138
+ WHERE DIGEST_TEXT IS NOT NULL
139
+ ORDER BY SUM_TIMER_WAIT DESC
140
+ LIMIT ${limit}
137
141
  `;
138
142
  const results = await this.db.query(query);
139
143
  return {
140
- status: 'success',
144
+ status: "success",
141
145
  data: results,
142
- queryLog: this.db.getFormattedQueryLogs(1)
143
146
  };
144
147
  }
145
148
  catch (error) {
146
149
  return {
147
- status: 'error',
150
+ status: "error",
148
151
  error: error.message,
149
- queryLog: this.db.getFormattedQueryLogs(1)
150
152
  };
151
153
  }
152
154
  }
@@ -157,36 +159,37 @@ class PerformanceTools {
157
159
  try {
158
160
  const limit = params?.limit || 10;
159
161
  if (!Number.isInteger(limit) || limit <= 0 || limit > 100) {
160
- return { status: 'error', error: 'Limit must be a positive integer between 1 and 100' };
161
- }
162
- const query = `
163
- SELECT
164
- DIGEST_TEXT as query_pattern,
165
- COUNT_STAR as execution_count,
166
- ROUND(AVG_TIMER_WAIT / 1000000000000, 6) as avg_execution_time_sec,
167
- ROUND(MAX_TIMER_WAIT / 1000000000000, 6) as max_execution_time_sec,
168
- ROUND(SUM_TIMER_WAIT / 1000000000000, 6) as total_execution_time_sec,
169
- SUM_ROWS_EXAMINED as rows_examined,
170
- SUM_ROWS_SENT as rows_sent,
171
- FIRST_SEEN,
172
- LAST_SEEN
173
- FROM performance_schema.events_statements_summary_by_digest
174
- WHERE DIGEST_TEXT IS NOT NULL
175
- ORDER BY COUNT_STAR DESC
176
- LIMIT ${limit}
162
+ return {
163
+ status: "error",
164
+ error: "Limit must be a positive integer between 1 and 100",
165
+ };
166
+ }
167
+ const query = `
168
+ SELECT
169
+ DIGEST_TEXT as query_pattern,
170
+ COUNT_STAR as execution_count,
171
+ ROUND(AVG_TIMER_WAIT / 1000000000000, 6) as avg_execution_time_sec,
172
+ ROUND(MAX_TIMER_WAIT / 1000000000000, 6) as max_execution_time_sec,
173
+ ROUND(SUM_TIMER_WAIT / 1000000000000, 6) as total_execution_time_sec,
174
+ SUM_ROWS_EXAMINED as rows_examined,
175
+ SUM_ROWS_SENT as rows_sent,
176
+ FIRST_SEEN,
177
+ LAST_SEEN
178
+ FROM performance_schema.events_statements_summary_by_digest
179
+ WHERE DIGEST_TEXT IS NOT NULL
180
+ ORDER BY COUNT_STAR DESC
181
+ LIMIT ${limit}
177
182
  `;
178
183
  const results = await this.db.query(query);
179
184
  return {
180
- status: 'success',
185
+ status: "success",
181
186
  data: results,
182
- queryLog: this.db.getFormattedQueryLogs(1)
183
187
  };
184
188
  }
185
189
  catch (error) {
186
190
  return {
187
- status: 'error',
191
+ status: "error",
188
192
  error: error.message,
189
- queryLog: this.db.getFormattedQueryLogs(1)
190
193
  };
191
194
  }
192
195
  }
@@ -198,42 +201,46 @@ class PerformanceTools {
198
201
  const limit = params?.limit || 10;
199
202
  const thresholdSec = params?.threshold_seconds || 1;
200
203
  if (!Number.isInteger(limit) || limit <= 0 || limit > 100) {
201
- return { status: 'error', error: 'Limit must be a positive integer between 1 and 100' };
204
+ return {
205
+ status: "error",
206
+ error: "Limit must be a positive integer between 1 and 100",
207
+ };
202
208
  }
203
- if (typeof thresholdSec !== 'number' || thresholdSec <= 0) {
204
- return { status: 'error', error: 'Threshold must be a positive number' };
209
+ if (typeof thresholdSec !== "number" || thresholdSec <= 0) {
210
+ return {
211
+ status: "error",
212
+ error: "Threshold must be a positive number",
213
+ };
205
214
  }
206
215
  const thresholdPico = thresholdSec * 1000000000000;
207
- const query = `
208
- SELECT
209
- DIGEST_TEXT as query_pattern,
210
- COUNT_STAR as execution_count,
211
- ROUND(AVG_TIMER_WAIT / 1000000000000, 6) as avg_execution_time_sec,
212
- ROUND(MAX_TIMER_WAIT / 1000000000000, 6) as max_execution_time_sec,
213
- ROUND(SUM_TIMER_WAIT / 1000000000000, 6) as total_execution_time_sec,
214
- ROUND(SUM_LOCK_TIME / 1000000000000, 6) as total_lock_time_sec,
215
- SUM_ROWS_EXAMINED as rows_examined,
216
- SUM_ROWS_SENT as rows_sent,
217
- SUM_NO_INDEX_USED as no_index_used_count,
218
- FIRST_SEEN,
219
- LAST_SEEN
220
- FROM performance_schema.events_statements_summary_by_digest
221
- WHERE DIGEST_TEXT IS NOT NULL AND AVG_TIMER_WAIT > ${thresholdPico}
222
- ORDER BY AVG_TIMER_WAIT DESC
223
- LIMIT ${limit}
216
+ const query = `
217
+ SELECT
218
+ DIGEST_TEXT as query_pattern,
219
+ COUNT_STAR as execution_count,
220
+ ROUND(AVG_TIMER_WAIT / 1000000000000, 6) as avg_execution_time_sec,
221
+ ROUND(MAX_TIMER_WAIT / 1000000000000, 6) as max_execution_time_sec,
222
+ ROUND(SUM_TIMER_WAIT / 1000000000000, 6) as total_execution_time_sec,
223
+ ROUND(SUM_LOCK_TIME / 1000000000000, 6) as total_lock_time_sec,
224
+ SUM_ROWS_EXAMINED as rows_examined,
225
+ SUM_ROWS_SENT as rows_sent,
226
+ SUM_NO_INDEX_USED as no_index_used_count,
227
+ FIRST_SEEN,
228
+ LAST_SEEN
229
+ FROM performance_schema.events_statements_summary_by_digest
230
+ WHERE DIGEST_TEXT IS NOT NULL AND AVG_TIMER_WAIT > ${thresholdPico}
231
+ ORDER BY AVG_TIMER_WAIT DESC
232
+ LIMIT ${limit}
224
233
  `;
225
234
  const results = await this.db.query(query);
226
235
  return {
227
- status: 'success',
236
+ status: "success",
228
237
  data: results,
229
- queryLog: this.db.getFormattedQueryLogs(1)
230
238
  };
231
239
  }
232
240
  catch (error) {
233
241
  return {
234
- status: 'error',
242
+ status: "error",
235
243
  error: error.message,
236
- queryLog: this.db.getFormattedQueryLogs(1)
237
244
  };
238
245
  }
239
246
  }
@@ -245,23 +252,26 @@ class PerformanceTools {
245
252
  const limit = params?.limit || 20;
246
253
  const schema = params?.table_schema;
247
254
  if (!Number.isInteger(limit) || limit <= 0 || limit > 100) {
248
- return { status: 'error', error: 'Limit must be a positive integer between 1 and 100' };
249
- }
250
- let query = `
251
- SELECT
252
- OBJECT_SCHEMA as table_schema,
253
- OBJECT_NAME as table_name,
254
- COUNT_READ as read_operations,
255
- COUNT_WRITE as write_operations,
256
- COUNT_FETCH as fetch_operations,
257
- COUNT_INSERT as insert_operations,
258
- COUNT_UPDATE as update_operations,
259
- COUNT_DELETE as delete_operations,
260
- ROUND(SUM_TIMER_READ / 1000000000000, 6) as total_read_time_sec,
261
- ROUND(SUM_TIMER_WRITE / 1000000000000, 6) as total_write_time_sec,
262
- ROUND(SUM_TIMER_FETCH / 1000000000000, 6) as total_fetch_time_sec
263
- FROM performance_schema.table_io_waits_summary_by_table
264
- WHERE OBJECT_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')
255
+ return {
256
+ status: "error",
257
+ error: "Limit must be a positive integer between 1 and 100",
258
+ };
259
+ }
260
+ let query = `
261
+ SELECT
262
+ OBJECT_SCHEMA as table_schema,
263
+ OBJECT_NAME as table_name,
264
+ COUNT_READ as read_operations,
265
+ COUNT_WRITE as write_operations,
266
+ COUNT_FETCH as fetch_operations,
267
+ COUNT_INSERT as insert_operations,
268
+ COUNT_UPDATE as update_operations,
269
+ COUNT_DELETE as delete_operations,
270
+ ROUND(SUM_TIMER_READ / 1000000000000, 6) as total_read_time_sec,
271
+ ROUND(SUM_TIMER_WRITE / 1000000000000, 6) as total_write_time_sec,
272
+ ROUND(SUM_TIMER_FETCH / 1000000000000, 6) as total_fetch_time_sec
273
+ FROM performance_schema.table_io_waits_summary_by_table
274
+ WHERE OBJECT_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')
265
275
  `;
266
276
  if (schema) {
267
277
  query += ` AND OBJECT_SCHEMA = '${schema.replace(/'/g, "''")}'`;
@@ -269,16 +279,14 @@ class PerformanceTools {
269
279
  query += ` ORDER BY (COUNT_READ + COUNT_WRITE) DESC LIMIT ${limit}`;
270
280
  const results = await this.db.query(query);
271
281
  return {
272
- status: 'success',
282
+ status: "success",
273
283
  data: results,
274
- queryLog: this.db.getFormattedQueryLogs(1)
275
284
  };
276
285
  }
277
286
  catch (error) {
278
287
  return {
279
- status: 'error',
288
+ status: "error",
280
289
  error: error.message,
281
- queryLog: this.db.getFormattedQueryLogs(1)
282
290
  };
283
291
  }
284
292
  }
@@ -290,23 +298,26 @@ class PerformanceTools {
290
298
  const limit = params?.limit || 20;
291
299
  const schema = params?.table_schema;
292
300
  if (!Number.isInteger(limit) || limit <= 0 || limit > 100) {
293
- return { status: 'error', error: 'Limit must be a positive integer between 1 and 100' };
294
- }
295
- let query = `
296
- SELECT
297
- OBJECT_SCHEMA as table_schema,
298
- OBJECT_NAME as table_name,
299
- INDEX_NAME as index_name,
300
- COUNT_STAR as usage_count,
301
- COUNT_READ as read_count,
302
- COUNT_WRITE as write_count,
303
- COUNT_FETCH as fetch_count,
304
- COUNT_INSERT as insert_count,
305
- COUNT_UPDATE as update_count,
306
- COUNT_DELETE as delete_count
307
- FROM performance_schema.table_io_waits_summary_by_index_usage
308
- WHERE OBJECT_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')
309
- AND INDEX_NAME IS NOT NULL
301
+ return {
302
+ status: "error",
303
+ error: "Limit must be a positive integer between 1 and 100",
304
+ };
305
+ }
306
+ let query = `
307
+ SELECT
308
+ OBJECT_SCHEMA as table_schema,
309
+ OBJECT_NAME as table_name,
310
+ INDEX_NAME as index_name,
311
+ COUNT_STAR as usage_count,
312
+ COUNT_READ as read_count,
313
+ COUNT_WRITE as write_count,
314
+ COUNT_FETCH as fetch_count,
315
+ COUNT_INSERT as insert_count,
316
+ COUNT_UPDATE as update_count,
317
+ COUNT_DELETE as delete_count
318
+ FROM performance_schema.table_io_waits_summary_by_index_usage
319
+ WHERE OBJECT_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')
320
+ AND INDEX_NAME IS NOT NULL
310
321
  `;
311
322
  if (schema) {
312
323
  query += ` AND OBJECT_SCHEMA = '${schema.replace(/'/g, "''")}'`;
@@ -314,16 +325,14 @@ class PerformanceTools {
314
325
  query += ` ORDER BY COUNT_STAR DESC LIMIT ${limit}`;
315
326
  const results = await this.db.query(query);
316
327
  return {
317
- status: 'success',
328
+ status: "success",
318
329
  data: results,
319
- queryLog: this.db.getFormattedQueryLogs(1)
320
330
  };
321
331
  }
322
332
  catch (error) {
323
333
  return {
324
- status: 'error',
334
+ status: "error",
325
335
  error: error.message,
326
- queryLog: this.db.getFormattedQueryLogs(1)
327
336
  };
328
337
  }
329
338
  }
@@ -333,26 +342,26 @@ class PerformanceTools {
333
342
  async getUnusedIndexes(params) {
334
343
  try {
335
344
  const schema = params?.table_schema;
336
- let query = `
337
- SELECT
338
- t.TABLE_SCHEMA as table_schema,
339
- t.TABLE_NAME as table_name,
340
- s.INDEX_NAME as index_name,
341
- s.COLUMN_NAME as column_name,
342
- s.SEQ_IN_INDEX as sequence_in_index,
343
- s.NON_UNIQUE as is_non_unique
344
- FROM information_schema.STATISTICS s
345
- LEFT JOIN performance_schema.table_io_waits_summary_by_index_usage p
346
- ON s.TABLE_SCHEMA = p.OBJECT_SCHEMA
347
- AND s.TABLE_NAME = p.OBJECT_NAME
348
- AND s.INDEX_NAME = p.INDEX_NAME
349
- JOIN information_schema.TABLES t
350
- ON s.TABLE_SCHEMA = t.TABLE_SCHEMA
351
- AND s.TABLE_NAME = t.TABLE_NAME
352
- WHERE s.TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')
353
- AND s.INDEX_NAME != 'PRIMARY'
354
- AND (p.INDEX_NAME IS NULL OR p.COUNT_STAR = 0)
355
- AND t.TABLE_TYPE = 'BASE TABLE'
345
+ let query = `
346
+ SELECT
347
+ t.TABLE_SCHEMA as table_schema,
348
+ t.TABLE_NAME as table_name,
349
+ s.INDEX_NAME as index_name,
350
+ s.COLUMN_NAME as column_name,
351
+ s.SEQ_IN_INDEX as sequence_in_index,
352
+ s.NON_UNIQUE as is_non_unique
353
+ FROM information_schema.STATISTICS s
354
+ LEFT JOIN performance_schema.table_io_waits_summary_by_index_usage p
355
+ ON s.TABLE_SCHEMA = p.OBJECT_SCHEMA
356
+ AND s.TABLE_NAME = p.OBJECT_NAME
357
+ AND s.INDEX_NAME = p.INDEX_NAME
358
+ JOIN information_schema.TABLES t
359
+ ON s.TABLE_SCHEMA = t.TABLE_SCHEMA
360
+ AND s.TABLE_NAME = t.TABLE_NAME
361
+ WHERE s.TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')
362
+ AND s.INDEX_NAME != 'PRIMARY'
363
+ AND (p.INDEX_NAME IS NULL OR p.COUNT_STAR = 0)
364
+ AND t.TABLE_TYPE = 'BASE TABLE'
356
365
  `;
357
366
  if (schema) {
358
367
  query += ` AND s.TABLE_SCHEMA = '${schema.replace(/'/g, "''")}'`;
@@ -360,16 +369,14 @@ class PerformanceTools {
360
369
  query += ` ORDER BY s.TABLE_SCHEMA, s.TABLE_NAME, s.INDEX_NAME, s.SEQ_IN_INDEX`;
361
370
  const results = await this.db.query(query);
362
371
  return {
363
- status: 'success',
372
+ status: "success",
364
373
  data: results,
365
- queryLog: this.db.getFormattedQueryLogs(1)
366
374
  };
367
375
  }
368
376
  catch (error) {
369
377
  return {
370
- status: 'error',
378
+ status: "error",
371
379
  error: error.message,
372
- queryLog: this.db.getFormattedQueryLogs(1)
373
380
  };
374
381
  }
375
382
  }
@@ -378,25 +385,25 @@ class PerformanceTools {
378
385
  */
379
386
  async getConnectionPoolStats() {
380
387
  try {
381
- const statusQuery = `
382
- SHOW GLOBAL STATUS WHERE Variable_name IN (
383
- 'Threads_connected', 'Threads_running', 'Threads_created', 'Threads_cached',
384
- 'Connections', 'Max_used_connections', 'Max_used_connections_time',
385
- 'Aborted_connects', 'Aborted_clients', 'Connection_errors_max_connections'
386
- )
388
+ const statusQuery = `
389
+ SHOW GLOBAL STATUS WHERE Variable_name IN (
390
+ 'Threads_connected', 'Threads_running', 'Threads_created', 'Threads_cached',
391
+ 'Connections', 'Max_used_connections', 'Max_used_connections_time',
392
+ 'Aborted_connects', 'Aborted_clients', 'Connection_errors_max_connections'
393
+ )
387
394
  `;
388
- const variablesQuery = `
389
- SHOW GLOBAL VARIABLES WHERE Variable_name IN (
390
- 'max_connections', 'thread_cache_size', 'connect_timeout',
391
- 'wait_timeout', 'interactive_timeout'
392
- )
395
+ const variablesQuery = `
396
+ SHOW GLOBAL VARIABLES WHERE Variable_name IN (
397
+ 'max_connections', 'thread_cache_size', 'connect_timeout',
398
+ 'wait_timeout', 'interactive_timeout'
399
+ )
393
400
  `;
394
401
  const statusResult = await this.db.query(statusQuery);
395
402
  const variablesResult = await this.db.query(variablesQuery);
396
403
  const stats = {
397
404
  current_status: {},
398
405
  configuration: {},
399
- health_indicators: {}
406
+ health_indicators: {},
400
407
  };
401
408
  for (const row of statusResult) {
402
409
  stats.current_status[row.Variable_name.toLowerCase()] = row.Value;
@@ -405,36 +412,40 @@ class PerformanceTools {
405
412
  stats.configuration[row.Variable_name.toLowerCase()] = row.Value;
406
413
  }
407
414
  // Calculate health indicators
408
- const threadsConnected = parseInt(stats.current_status.threads_connected || '0');
409
- const maxConnections = parseInt(stats.configuration.max_connections || '0');
410
- const maxUsedConnections = parseInt(stats.current_status.max_used_connections || '0');
415
+ const threadsConnected = parseInt(stats.current_status.threads_connected || "0");
416
+ const maxConnections = parseInt(stats.configuration.max_connections || "0");
417
+ const maxUsedConnections = parseInt(stats.current_status.max_used_connections || "0");
411
418
  if (maxConnections > 0) {
412
- stats.health_indicators.connection_usage_percentage = ((threadsConnected / maxConnections) * 100).toFixed(2) + '%';
413
- stats.health_indicators.max_usage_percentage = ((maxUsedConnections / maxConnections) * 100).toFixed(2) + '%';
414
- stats.health_indicators.available_connections = maxConnections - threadsConnected;
419
+ stats.health_indicators.connection_usage_percentage =
420
+ ((threadsConnected / maxConnections) * 100).toFixed(2) + "%";
421
+ stats.health_indicators.max_usage_percentage =
422
+ ((maxUsedConnections / maxConnections) * 100).toFixed(2) + "%";
423
+ stats.health_indicators.available_connections =
424
+ maxConnections - threadsConnected;
415
425
  }
416
426
  // Connection efficiency
417
- const totalConnections = parseInt(stats.current_status.connections || '0');
418
- const abortedConnects = parseInt(stats.current_status.aborted_connects || '0');
427
+ const totalConnections = parseInt(stats.current_status.connections || "0");
428
+ const abortedConnects = parseInt(stats.current_status.aborted_connects || "0");
419
429
  if (totalConnections > 0) {
420
- stats.health_indicators.aborted_connection_percentage = ((abortedConnects / totalConnections) * 100).toFixed(4) + '%';
430
+ stats.health_indicators.aborted_connection_percentage =
431
+ ((abortedConnects / totalConnections) * 100).toFixed(4) + "%";
421
432
  }
422
433
  // Thread cache efficiency
423
- const threadsCreated = parseInt(stats.current_status.threads_created || '0');
434
+ const threadsCreated = parseInt(stats.current_status.threads_created || "0");
424
435
  if (totalConnections > 0) {
425
- stats.health_indicators.thread_cache_hit_rate = (((totalConnections - threadsCreated) / totalConnections) * 100).toFixed(2) + '%';
436
+ stats.health_indicators.thread_cache_hit_rate =
437
+ (((totalConnections - threadsCreated) / totalConnections) *
438
+ 100).toFixed(2) + "%";
426
439
  }
427
440
  return {
428
- status: 'success',
441
+ status: "success",
429
442
  data: stats,
430
- queryLog: this.db.getFormattedQueryLogs(2)
431
443
  };
432
444
  }
433
445
  catch (error) {
434
446
  return {
435
- status: 'error',
447
+ status: "error",
436
448
  error: error.message,
437
- queryLog: this.db.getFormattedQueryLogs(1)
438
449
  };
439
450
  }
440
451
  }
@@ -444,14 +455,14 @@ class PerformanceTools {
444
455
  async getDatabaseHealthCheck() {
445
456
  try {
446
457
  const health = {
447
- overall_status: 'healthy',
458
+ overall_status: "healthy",
448
459
  checks: [],
449
460
  warnings: [],
450
- errors: []
461
+ errors: [],
451
462
  };
452
463
  // Check 1: Connection usage
453
- const connQuery = `
454
- SHOW GLOBAL STATUS WHERE Variable_name IN ('Threads_connected', 'Max_used_connections')
464
+ const connQuery = `
465
+ SHOW GLOBAL STATUS WHERE Variable_name IN ('Threads_connected', 'Max_used_connections')
455
466
  `;
456
467
  const maxConnQuery = `SHOW GLOBAL VARIABLES WHERE Variable_name = 'max_connections'`;
457
468
  const connResult = await this.db.query(connQuery);
@@ -460,9 +471,9 @@ class PerformanceTools {
460
471
  let maxUsedConnections = 0;
461
472
  let maxConnections = 0;
462
473
  for (const row of connResult) {
463
- if (row.Variable_name === 'Threads_connected')
474
+ if (row.Variable_name === "Threads_connected")
464
475
  threadsConnected = parseInt(row.Value);
465
- if (row.Variable_name === 'Max_used_connections')
476
+ if (row.Variable_name === "Max_used_connections")
466
477
  maxUsedConnections = parseInt(row.Value);
467
478
  }
468
479
  for (const row of maxConnResult) {
@@ -471,122 +482,124 @@ class PerformanceTools {
471
482
  const connUsage = (threadsConnected / maxConnections) * 100;
472
483
  const maxConnUsage = (maxUsedConnections / maxConnections) * 100;
473
484
  health.checks.push({
474
- name: 'Connection Usage',
475
- status: connUsage < 80 ? 'healthy' : connUsage < 90 ? 'warning' : 'critical',
485
+ name: "Connection Usage",
486
+ status: connUsage < 80 ? "healthy" : connUsage < 90 ? "warning" : "critical",
476
487
  current: threadsConnected,
477
488
  max: maxConnections,
478
- usage_percentage: connUsage.toFixed(2) + '%'
489
+ usage_percentage: connUsage.toFixed(2) + "%",
479
490
  });
480
491
  if (connUsage >= 90) {
481
- health.errors.push('Connection usage is critically high (>90%)');
482
- health.overall_status = 'critical';
492
+ health.errors.push("Connection usage is critically high (>90%)");
493
+ health.overall_status = "critical";
483
494
  }
484
495
  else if (connUsage >= 80) {
485
- health.warnings.push('Connection usage is high (>80%)');
486
- if (health.overall_status === 'healthy')
487
- health.overall_status = 'warning';
496
+ health.warnings.push("Connection usage is high (>80%)");
497
+ if (health.overall_status === "healthy")
498
+ health.overall_status = "warning";
488
499
  }
489
500
  // Check 2: Buffer pool hit ratio
490
- const bufferQuery = `
491
- SHOW GLOBAL STATUS WHERE Variable_name IN ('Innodb_buffer_pool_read_requests', 'Innodb_buffer_pool_reads')
501
+ const bufferQuery = `
502
+ SHOW GLOBAL STATUS WHERE Variable_name IN ('Innodb_buffer_pool_read_requests', 'Innodb_buffer_pool_reads')
492
503
  `;
493
504
  const bufferResult = await this.db.query(bufferQuery);
494
505
  let readRequests = 0;
495
506
  let reads = 0;
496
507
  for (const row of bufferResult) {
497
- if (row.Variable_name === 'Innodb_buffer_pool_read_requests')
508
+ if (row.Variable_name === "Innodb_buffer_pool_read_requests")
498
509
  readRequests = parseInt(row.Value);
499
- if (row.Variable_name === 'Innodb_buffer_pool_reads')
510
+ if (row.Variable_name === "Innodb_buffer_pool_reads")
500
511
  reads = parseInt(row.Value);
501
512
  }
502
513
  const hitRatio = readRequests > 0 ? ((readRequests - reads) / readRequests) * 100 : 100;
503
514
  health.checks.push({
504
- name: 'Buffer Pool Hit Ratio',
505
- status: hitRatio > 95 ? 'healthy' : hitRatio > 85 ? 'warning' : 'critical',
506
- hit_ratio: hitRatio.toFixed(2) + '%'
515
+ name: "Buffer Pool Hit Ratio",
516
+ status: hitRatio > 95 ? "healthy" : hitRatio > 85 ? "warning" : "critical",
517
+ hit_ratio: hitRatio.toFixed(2) + "%",
507
518
  });
508
519
  if (hitRatio <= 85) {
509
- health.errors.push('Buffer pool hit ratio is too low (<85%)');
510
- health.overall_status = 'critical';
520
+ health.errors.push("Buffer pool hit ratio is too low (<85%)");
521
+ health.overall_status = "critical";
511
522
  }
512
523
  else if (hitRatio <= 95) {
513
- health.warnings.push('Buffer pool hit ratio could be improved (<95%)');
514
- if (health.overall_status === 'healthy')
515
- health.overall_status = 'warning';
524
+ health.warnings.push("Buffer pool hit ratio could be improved (<95%)");
525
+ if (health.overall_status === "healthy")
526
+ health.overall_status = "warning";
516
527
  }
517
528
  // Check 3: Aborted connections
518
- const abortQuery = `
519
- SHOW GLOBAL STATUS WHERE Variable_name IN ('Connections', 'Aborted_connects')
529
+ const abortQuery = `
530
+ SHOW GLOBAL STATUS WHERE Variable_name IN ('Connections', 'Aborted_connects')
520
531
  `;
521
532
  const abortResult = await this.db.query(abortQuery);
522
533
  let totalConnections = 0;
523
534
  let abortedConnects = 0;
524
535
  for (const row of abortResult) {
525
- if (row.Variable_name === 'Connections')
536
+ if (row.Variable_name === "Connections")
526
537
  totalConnections = parseInt(row.Value);
527
- if (row.Variable_name === 'Aborted_connects')
538
+ if (row.Variable_name === "Aborted_connects")
528
539
  abortedConnects = parseInt(row.Value);
529
540
  }
530
541
  const abortRate = totalConnections > 0 ? (abortedConnects / totalConnections) * 100 : 0;
531
542
  health.checks.push({
532
- name: 'Aborted Connections',
533
- status: abortRate < 1 ? 'healthy' : abortRate < 5 ? 'warning' : 'critical',
543
+ name: "Aborted Connections",
544
+ status: abortRate < 1 ? "healthy" : abortRate < 5 ? "warning" : "critical",
534
545
  aborted: abortedConnects,
535
546
  total: totalConnections,
536
- abort_rate: abortRate.toFixed(4) + '%'
547
+ abort_rate: abortRate.toFixed(4) + "%",
537
548
  });
538
549
  if (abortRate >= 5) {
539
- health.errors.push('High rate of aborted connections (>5%)');
540
- if (health.overall_status !== 'critical')
541
- health.overall_status = 'warning';
550
+ health.errors.push("High rate of aborted connections (>5%)");
551
+ if (health.overall_status !== "critical")
552
+ health.overall_status = "warning";
542
553
  }
543
554
  else if (abortRate >= 1) {
544
- health.warnings.push('Elevated rate of aborted connections (>1%)');
545
- if (health.overall_status === 'healthy')
546
- health.overall_status = 'warning';
555
+ health.warnings.push("Elevated rate of aborted connections (>1%)");
556
+ if (health.overall_status === "healthy")
557
+ health.overall_status = "warning";
547
558
  }
548
559
  // Check 4: Slow queries
549
- const slowQuery = `
550
- SHOW GLOBAL STATUS WHERE Variable_name IN ('Questions', 'Slow_queries')
560
+ const slowQuery = `
561
+ SHOW GLOBAL STATUS WHERE Variable_name IN ('Questions', 'Slow_queries')
551
562
  `;
552
563
  const slowResult = await this.db.query(slowQuery);
553
564
  let questions = 0;
554
565
  let slowQueries = 0;
555
566
  for (const row of slowResult) {
556
- if (row.Variable_name === 'Questions')
567
+ if (row.Variable_name === "Questions")
557
568
  questions = parseInt(row.Value);
558
- if (row.Variable_name === 'Slow_queries')
569
+ if (row.Variable_name === "Slow_queries")
559
570
  slowQueries = parseInt(row.Value);
560
571
  }
561
572
  const slowQueryRate = questions > 0 ? (slowQueries / questions) * 100 : 0;
562
573
  health.checks.push({
563
- name: 'Slow Queries',
564
- status: slowQueryRate < 1 ? 'healthy' : slowQueryRate < 5 ? 'warning' : 'critical',
574
+ name: "Slow Queries",
575
+ status: slowQueryRate < 1
576
+ ? "healthy"
577
+ : slowQueryRate < 5
578
+ ? "warning"
579
+ : "critical",
565
580
  slow_queries: slowQueries,
566
581
  total_queries: questions,
567
- slow_query_rate: slowQueryRate.toFixed(4) + '%'
582
+ slow_query_rate: slowQueryRate.toFixed(4) + "%",
568
583
  });
569
584
  if (slowQueryRate >= 5) {
570
- health.warnings.push('High rate of slow queries (>5%)');
571
- if (health.overall_status === 'healthy')
572
- health.overall_status = 'warning';
585
+ health.warnings.push("High rate of slow queries (>5%)");
586
+ if (health.overall_status === "healthy")
587
+ health.overall_status = "warning";
573
588
  }
574
589
  else if (slowQueryRate >= 1) {
575
- health.warnings.push('Elevated rate of slow queries (>1%)');
576
- if (health.overall_status === 'healthy')
577
- health.overall_status = 'warning';
590
+ health.warnings.push("Elevated rate of slow queries (>1%)");
591
+ if (health.overall_status === "healthy")
592
+ health.overall_status = "warning";
578
593
  }
579
594
  return {
580
- status: 'success',
595
+ status: "success",
581
596
  data: health,
582
- queryLog: this.db.getFormattedQueryLogs(6)
583
597
  };
584
598
  }
585
599
  catch (error) {
586
600
  return {
587
- status: 'error',
601
+ status: "error",
588
602
  error: error.message,
589
- queryLog: this.db.getFormattedQueryLogs(1)
590
603
  };
591
604
  }
592
605
  }
@@ -597,25 +610,23 @@ class PerformanceTools {
597
610
  try {
598
611
  // Truncate performance schema summary tables
599
612
  const tables = [
600
- 'events_statements_summary_by_digest',
601
- 'events_statements_summary_global_by_event_name',
602
- 'table_io_waits_summary_by_table',
603
- 'table_io_waits_summary_by_index_usage'
613
+ "events_statements_summary_by_digest",
614
+ "events_statements_summary_global_by_event_name",
615
+ "table_io_waits_summary_by_table",
616
+ "table_io_waits_summary_by_index_usage",
604
617
  ];
605
618
  for (const table of tables) {
606
619
  await this.db.query(`TRUNCATE TABLE performance_schema.${table}`);
607
620
  }
608
621
  return {
609
- status: 'success',
610
- message: 'Performance statistics reset successfully',
611
- queryLog: this.db.getFormattedQueryLogs(tables.length)
622
+ status: "success",
623
+ message: "Performance statistics reset successfully",
612
624
  };
613
625
  }
614
626
  catch (error) {
615
627
  return {
616
- status: 'error',
628
+ status: "error",
617
629
  error: error.message,
618
- queryLog: this.db.getFormattedQueryLogs(1)
619
630
  };
620
631
  }
621
632
  }