Skip to content

Commit 35bba2a

Browse files
committed
Merge branch 'dev' into emma/TestingTestNewQueryPage
2 parents 2e68e68 + f12013d commit 35bba2a

File tree

15 files changed

+400
-140
lines changed

15 files changed

+400
-140
lines changed

server/controllers/mysqlData.controller.ts

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,51 @@ const mysqlController = {
191191
// Step 3: Parse the output for actual times
192192
let totalExecutionTime = 0;
193193

194+
// Regex to extract the additional metrics
195+
const match = row.match(
196+
/-> (\w+ \w+) on (\w+).*cost=([\d.]+) rows=(\d+).*actual time=([\d.]+)\.\.([\d.]+) rows=(\d+)/
197+
);
198+
199+
if (match) {
200+
const [
201+
_,
202+
nodeType,
203+
relationName,
204+
totalCost,
205+
planRows,
206+
actualTimeStart,
207+
actualTimeEnd,
208+
actualRows,
209+
] = match;
210+
211+
const actualTotalTime = parseFloat(actualTimeEnd) - parseFloat(actualTimeStart);
212+
console.log({
213+
nodeType,
214+
relationName,
215+
totalCost: parseFloat(totalCost),
216+
planRows: parseInt(planRows),
217+
actualRows: parseInt(actualRows),
218+
actualTotalTime: parseFloat(actualTotalTime.toFixed(4)), // in ms
219+
});
220+
221+
// MySQL, doesn't have planningTime, sharedHit, and sharedRead so setting as null
222+
const moreMets: Array<object> = [
223+
{
224+
planningTime: null,
225+
actualTotalTime: parseFloat(actualTotalTime.toFixed(4)),
226+
totalCost: parseFloat(totalCost),
227+
nodeType,
228+
relationName,
229+
planRows: parseInt(planRows),
230+
actualRows: parseInt(actualRows),
231+
sharedHit: null,
232+
sharedRead: null,
233+
},
234+
];
235+
236+
res.locals.otherMetrics = moreMets;
237+
}
238+
194239
// Regex to extract actual time values from the EXPLAIN ANALYZE output
195240
const times = row.match(/actual time=(\d+\.\d+)\.\.(\d+\.\d+)/);
196241
// console.log('🌟 RESULT OF TIMES', times);
@@ -232,10 +277,6 @@ const mysqlController = {
232277
year: 'numeric',
233278
month: 'long',
234279
day: 'numeric',
235-
// hour: 'numeric',
236-
// minute: 'numeric',
237-
// second: 'numeric',
238-
// hour12: true, // Use 12-hour time format
239280
timeZone: 'America/New_York', // Set to US Eastern Time
240281
};
241282

@@ -261,17 +302,27 @@ const mysqlController = {
261302
mysqlSaveQuery: async (req: Request, res: Response, next: NextFunction) => {
262303
try {
263304
// grab queryString from FE
264-
console.log('REACHED mysqlSaveQuery MIDDLEWARE');
265305
console.log('REQ QUERY: ', req.body.params);
266306
const { query_date, exec_time } = req.body.params.extractedQueryRes;
267307
const { queryString, queryName, hostname, database_name } =
268308
req.body.params.dbValues;
309+
const {
310+
planningTime,
311+
totalCost,
312+
actualTotalTime,
313+
nodeType,
314+
relationName,
315+
planRows,
316+
actualRows,
317+
sharedHit,
318+
sharedRead,
319+
} = req.body.params.moreMetrics;
269320

270321
const date = new Date(query_date);
271322
const formatDateForMySql = date.toISOString().split('T')[0];
272323
console.log('formatted date: ', formatDateForMySql);
273324

274-
const insertQueryStr = `INSERT INTO queries (query, db_link, exec_time, db_name, query_date, name) VALUES(?,?,?,?,?,?)`;
325+
const insertQueryStr = `INSERT INTO queries (query, db_link, exec_time, db_name, query_date, name, planning_time, total_cost, actual_total_time, node_type, relation_name, plan_rows, actual_rows, shared_hit_blocks, shared_read_blocks) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`;
275326

276327
const [saveQuery]: any = await pool.query(insertQueryStr, [
277328
queryString,
@@ -280,6 +331,15 @@ const mysqlController = {
280331
database_name,
281332
formatDateForMySql,
282333
queryName,
334+
planningTime,
335+
totalCost,
336+
actualTotalTime,
337+
nodeType,
338+
relationName,
339+
planRows,
340+
actualRows,
341+
sharedHit,
342+
sharedRead,
283343
]);
284344

285345
// Check if insertion was successful
@@ -295,6 +355,15 @@ const mysqlController = {
295355
database_name,
296356
formatDateForMySql,
297357
queryName,
358+
planningTime,
359+
totalCost,
360+
actualTotalTime,
361+
nodeType,
362+
relationName,
363+
planRows,
364+
actualRows,
365+
sharedHit,
366+
sharedRead,
298367
];
299368
return next();
300369
} else {

server/controllers/postgresData.controller.ts

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -183,35 +183,55 @@ const postgresController = {
183183
const testQuery = `EXPLAIN (FORMAT JSON, ANALYZE, VERBOSE, BUFFERS) ${queryString};`;
184184
const result = await PostgresGetMetrics.query(testQuery);
185185

186-
// console.log('🌟 result of testing: ', result);
187186
console.log('⭐️QUERY PLAN RESULT: ', result[0]['QUERY PLAN']);
187+
// 🌟🌟🌟
188+
/*
189+
Key Metrics Explanation:
190+
- Execution Time: total time it takes to execute the query (in ms)
191+
- Planning Time: time spent preparing the query plan (shows if the query is complex to plan)
192+
- Actual Total Time: actual runtime of query (helps show real-time performance)
193+
- Node Type (Seq Scan, Index Scan, Hash Join): tells you what scan/join is used - seq scan on big tables is a red flag
194+
- Relation Name: table being queried (context for which part is being scanned)
195+
- Plan Rows vs Actual Rows: estimated vs actual rows (if they differ a lot = bad stats or inefficient plan)
196+
- Shared Hit Blocks / Read Blocks: pages read from cache vs. disk (shows I/O behavior -> disk access = slower)
197+
*/
188198

189199
//pull exec time alone for the mysql update when storing queries
190200
const exec_time = result[0]['QUERY PLAN'][0]['Execution Time'];
191201
console.log('exec_time:', exec_time);
192202

193203
// Pull Execution time only
194-
const executionTime = `Execution Time: ${result[0]['QUERY PLAN'][0]['Execution Time']}ms`;
204+
const resObj = result[0]['QUERY PLAN'][0];
205+
const resObjPlan = resObj['Plan'];
206+
const executionTime = `Execution Time: ${resObj['Execution Time']}ms`;
195207
const namedQuery = `Query Name: ${queryName}`;
196208
const queryStr = `Query: ${queryString}`;
197209
// console.log('⏰ EXECUTION TIME METRIC', executionTime);
198210

211+
const otherMetrics: Array<object> = [
212+
{
213+
planningTime: resObj['Planning Time'],
214+
actualTotalTime: resObjPlan['Actual Total Time'],
215+
totalCost: resObjPlan['Total Cost'],
216+
nodeType: resObjPlan['Node Type'],
217+
relationName: resObjPlan['Relation Name'],
218+
planRows: resObjPlan['Plan Rows'],
219+
actualRows: resObjPlan['Actual Rows'],
220+
sharedHit: resObjPlan['Shared Hit Blocks'],
221+
sharedRead: resObjPlan['Shared Read Blocks'],
222+
},
223+
];
224+
// console.log('OTHER METRICCSSSS: ', otherMetrics);
225+
199226
// Create date metric to add to response
200227
const now = new Date();
201228
const options: any = {
202229
year: 'numeric',
203230
month: 'long',
204231
day: 'numeric',
205-
// hour: 'numeric',
206-
// minute: 'numeric',
207-
// second: 'numeric',
208-
// hour12: true, // Use 12-hour time format
209232
timeZone: 'America/New_York', // Set to US Eastern Time
210233
};
211234
const formattedDate = `Date Run: ${now.toLocaleString('en-US', options)}`;
212-
//console.log(`🗓️ TODAY'S DATE`, formattedDate);
213-
214-
// console.log('done w getMetrics controller');
215235

216236
/************* mysql querymetrics db insert for saved query page retrieval ******/
217237
// creating insert query for mysql query metrics db
@@ -220,8 +240,9 @@ const postgresController = {
220240
//const db_link = 'postgresql://postgres.gcfszuopjvbjtllgmenw:[email protected]:6543/postgres';
221241
//const db_name = 'dbSpy';
222242

223-
// Send query string, date, and execution time on response
243+
// Send query name, query string, date, and execution time on response
224244
res.locals.metrics = [namedQuery, queryStr, formattedDate, executionTime];
245+
res.locals.otherMetrics = otherMetrics;
225246
PostgresGetMetrics.destroy();
226247
return next();
227248
} catch (err) {
@@ -241,17 +262,24 @@ const postgresController = {
241262
const { query_date, exec_time } = req.body.params.extractedQueryRes;
242263
const { queryString, queryName, hostname, database_name } =
243264
req.body.params.dbValues;
265+
const {
266+
planningTime,
267+
totalCost,
268+
actualTotalTime,
269+
nodeType,
270+
relationName,
271+
planRows,
272+
actualRows,
273+
sharedHit,
274+
sharedRead,
275+
} = req.body.params.moreMetrics;
244276

245277
// converting date to DATE format (YYYY-MM-DD) for MySQL to insert into queries table
246278
const date = new Date(query_date);
247279
const formatDateForMySql = date.toISOString().split('T')[0];
248280
console.log('formatted date: ', formatDateForMySql);
249281

250-
// UNCOMMENT code below
251-
// const insertQueryStr = `INSERT INTO queries (query, db_link, exec_time, db_name, query_date, name) VALUES(?,?,?,?,?,?)`;
252-
253-
// DELETE this after Vicky does her PR (mock data)
254-
const insertQueryStr = `INSERT INTO queries (query, db_link, exec_time, db_name, query_date, name, planning_time, total_cost, actual_total_time, node_type, relation_name, plan_rows, actual_rows, shared_hit_blocks, shared_read_blocks) VALUES(?,?,?,?,?,?, 0, 0, 0, 'test', 'test', 0, 0, 0, 0)`;
282+
const insertQueryStr = `INSERT INTO queries (query, db_link, exec_time, db_name, query_date, name, planning_time, total_cost, actual_total_time, node_type, relation_name, plan_rows, actual_rows, shared_hit_blocks, shared_read_blocks) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`;
255283

256284
//connect to mysql pool imported from userModel and send the query to update table
257285
const [savingQuery]: any = await pool.query(insertQueryStr, [
@@ -261,6 +289,15 @@ const postgresController = {
261289
database_name,
262290
formatDateForMySql,
263291
queryName,
292+
planningTime,
293+
totalCost,
294+
actualTotalTime,
295+
nodeType,
296+
relationName,
297+
planRows,
298+
actualRows,
299+
sharedHit,
300+
sharedRead,
264301
]);
265302

266303
// Check if insertion was successful
@@ -276,6 +313,15 @@ const postgresController = {
276313
database_name,
277314
formatDateForMySql,
278315
queryName,
316+
planningTime,
317+
totalCost,
318+
actualTotalTime,
319+
nodeType,
320+
relationName,
321+
planRows,
322+
actualRows,
323+
sharedHit,
324+
sharedRead,
279325
];
280326
return next();
281327
} else {

server/controllers/save.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const saveController = {
3535
try {
3636
// query to get all necessary data to the FE
3737
// TODO - add in user data to filter per user
38-
const getQueries: string = `SELECT name, query, query_date, exec_time FROM queries`;
38+
const getQueries: string = `SELECT name, query, query_date, exec_time, planning_time, total_cost, actual_total_time, node_type, relation_name, plan_rows, actual_rows, shared_hit_blocks, shared_read_blocks FROM queries ORDER BY id DESC`;
3939
const queryVal = await pool.query(getQueries);
4040
res.locals.savedQueries = queryVal;
4141
return next();

server/routes/mysql.router.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ mysqlRouter.get(
1212
'/run-query',
1313
mysqlController.mysqlGetMetrics,
1414
(_req: Request, res: Response) => {
15-
return res.status(200).json(res.locals.metrics);
15+
return res.status(200).json(res.locals);
1616
}
1717
);
1818

server/routes/postgres.router.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ postgresRouter.get(
1616
'/run-query',
1717
postgresController.postgresGetMetrics,
1818
(_req: Request, res: Response) => {
19-
return res.status(200).json(res.locals.metrics);
19+
return res.status(200).json(res.locals);
2020
}
2121
);
2222

server/seed/seed.ts

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const loadData = async () => {
2828
\`type\` varchar(40) DEFAULT NULL,
2929
PRIMARY KEY (\`id\`),
3030
KEY \`FOREIGN\` (\`email\`)
31-
) ENGINE = InnoDB AUTO_INCREMENT = 40 DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci`;
31+
) ENGINE = InnoDB AUTO_INCREMENT = 40 DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci`;
3232
try {
3333
console.log('🔹 Seeding createUserTable...');
3434
await connection.promise().query(createUserTable);
@@ -39,14 +39,23 @@ const loadData = async () => {
3939

4040
const createQueriesTable: string = `CREATE TABLE
4141
\`queries\` (
42-
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
43-
\`email\` varchar(240) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
44-
\`query\` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
45-
\`db_link\` varchar(240) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
46-
\`db_name\` varchar(40) NOT NULL,
47-
\`exec_time\` double NOT NULL,
48-
\`query_date\` date DEFAULT NULL,
49-
\`name\` varchar(40) DEFAULT NULL,
42+
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
43+
\`email\` varchar(240) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
44+
\`query\` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
45+
\`db_link\` varchar(240) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
46+
\`db_name\` varchar(40) NOT NULL,
47+
\`exec_time\` double NOT NULL,
48+
\`query_date\` date DEFAULT NULL,
49+
\`name\` varchar(40) DEFAULT NULL,
50+
\`planning_time\` double DEFAULT NULL,
51+
\`total_cost\` double NOT NULL,
52+
\`actual_total_time\` double NOT NULL,
53+
\`node_type\` char(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
54+
\`relation_name\` varchar(240) NOT NULL,
55+
\`plan_rows\` int NOT NULL,
56+
\`actual_rows\` int NOT NULL,
57+
\`shared_hit_blocks\` int DEFAULT NULL,
58+
\`shared_read_blocks\` int DEFAULT NULL,
5059
PRIMARY KEY (\`id\`),
5160
KEY \`email_queries\` (\`email\`),
5261
CONSTRAINT \`email_queries\` FOREIGN KEY (\`email\`) REFERENCES \`users\` (\`email\`))`;
@@ -60,11 +69,11 @@ const loadData = async () => {
6069

6170
const createSavedDbTable: string = `CREATE TABLE
6271
\`saveddb\` (
63-
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
64-
\`SaveName\` varchar(240) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
65-
\`email\` varchar(240) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
66-
\`SaveData\` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
67-
\`TableData\` text,
72+
\`id\` int unsigned NOT NULL AUTO_INCREMENT,
73+
\`SaveName\` varchar(240) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
74+
\`email\` varchar(240) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
75+
\`SaveData\` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
76+
\`TableData\` text,
6877
PRIMARY KEY (\`id\`),
6978
KEY \`email\` (\`email\`),
7079
CONSTRAINT \`email\` FOREIGN KEY (\`email\`) REFERENCES \`users\` (\`email\`))`;

src/assets/sidebarClose.svg

Lines changed: 5 additions & 7 deletions
Loading

src/assets/sidebarOpen.svg

Lines changed: 5 additions & 15 deletions
Loading

src/components/DBDisplay/FeatureTab.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import axios, { AxiosResponse } from 'axios';
44
import { NavLink, useNavigate } from 'react-router-dom';
55
import { useNavStore } from '../../store/navStore';
66

7-
const linkbtn = 'mt-4 inline-block lg:mt-0 text-blue-200 hover:text-white mr-4';
8-
97
// Functions imported:
108
import parseSql from '../../parse';
119

0 commit comments

Comments
 (0)