Skip to content

Commit 5c8d5b9

Browse files
committed
chore: use json format isntead
1 parent c04de2c commit 5c8d5b9

1 file changed

Lines changed: 39 additions & 23 deletions

File tree

src/postgres/postgres.controller.ts

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -211,36 +211,52 @@ export class PostgresController {
211211

212212
const statRow = (statsResult as unknown as any[])[0];
213213

214-
// Try to get EXPLAIN plan (may fail for queries with parameters)
214+
// Try to get EXPLAIN plan for pev2 (https://github.com/dalibo/pev2)
215+
// We use ANALYZE for actual execution stats, but wrap in a transaction with ROLLBACK
216+
// to prevent any data modifications from DML queries
215217
let explainPlan: string | null = null;
216218
try {
217-
// First try with generic plan for parameterized queries
218-
const explainResult = await this.postgres.query<{
219-
"QUERY PLAN": string;
220-
}>(`
221-
EXPLAIN (FORMAT TEXT, GENERIC_PLAN true) ${statRow.query}
222-
`);
223-
explainPlan = (explainResult as unknown as any[])
224-
.map((row) => row["QUERY PLAN"])
225-
.join("\n");
219+
// First try with full ANALYZE in a rolled-back transaction for safety
220+
// This gives us actual execution stats for pev2 without persisting changes
221+
await this.postgres.query("BEGIN");
222+
try {
223+
const explainResult = await this.postgres.query<{
224+
"QUERY PLAN": any;
225+
}>(`
226+
EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON) ${statRow.query}
227+
`);
228+
await this.postgres.query("ROLLBACK");
229+
// Format the JSON nicely for pev2
230+
const jsonPlan = (explainResult as unknown as any[])[0]["QUERY PLAN"];
231+
explainPlan = JSON.stringify(jsonPlan, null, 2);
232+
} catch (analyzeError) {
233+
// Make sure to rollback on error
234+
await this.postgres.query("ROLLBACK");
235+
throw analyzeError;
236+
}
226237
} catch (error) {
227-
// If generic plan fails, try without it (works for non-parameterized queries)
238+
// If ANALYZE fails, try without it (still good for pev2, just no actual stats)
228239
try {
229240
const explainResult = await this.postgres.query<{
230-
"QUERY PLAN": string;
241+
"QUERY PLAN": any;
231242
}>(`
232-
EXPLAIN (FORMAT TEXT) ${statRow.query}
243+
EXPLAIN (COSTS, VERBOSE, BUFFERS, FORMAT JSON) ${statRow.query}
233244
`);
234-
explainPlan = (explainResult as unknown as any[])
235-
.map((row) => row["QUERY PLAN"])
236-
.join("\n");
237-
} catch (innerError) {
238-
// EXPLAIN failed - this is common for parameterized queries from pg_stat_statements
239-
// Return a helpful message instead of null
240-
explainPlan =
241-
"EXPLAIN plan cannot be generated for parameterized queries.\n\n" +
242-
"The query contains parameter placeholders ($1, $2, etc.) that cannot be explained without actual values.\n\n" +
243-
"To see the execution plan, run EXPLAIN with actual parameter values in psql or your database client.";
245+
const jsonPlan = (explainResult as unknown as any[])[0]["QUERY PLAN"];
246+
explainPlan = JSON.stringify(jsonPlan, null, 2);
247+
} catch {
248+
// Fall back to generic plan for parameterized queries
249+
try {
250+
const explainResult = await this.postgres.query<{
251+
"QUERY PLAN": any;
252+
}>(`
253+
EXPLAIN (FORMAT JSON, GENERIC_PLAN true) ${statRow.query}
254+
`);
255+
const jsonPlan = (explainResult as unknown as any[])[0]["QUERY PLAN"];
256+
explainPlan = JSON.stringify(jsonPlan, null, 2);
257+
} catch {
258+
// not able to get explain plan
259+
}
244260
}
245261
}
246262

0 commit comments

Comments
 (0)