Skip to content

fix(postgres): read EXPLAIN JSON output as json column#279

Merged
debba merged 2 commits into
mainfrom
fix/postgres-explain-json-deserialization
Jun 4, 2026
Merged

fix(postgres): read EXPLAIN JSON output as json column#279
debba merged 2 commits into
mainfrom
fix/postgres-explain-json-deserialization

Conversation

@debba
Copy link
Copy Markdown
Collaborator

@debba debba commented Jun 3, 2026

What

Fixes the "Explain Plan" feature for Postgres, which always failed with error deserializing column 0 (#276).

Why it broke

EXPLAIN (FORMAT JSON) ... returns the plan in a single column, and on Postgres that column is typed as json (OID 114), not text. The code read it straight into a String:

let plan_json_str: String = rows[0].try_get(0)...;

tokio-postgres refuses to deserialize a json column into a String, so the call errored out before the plan was ever parsed. This is server-side behaviour that's been stable since the FORMAT option landed in PG 9.0, so the feature never worked on any Postgres version — matching the report against PG 16 and 18.

Fix

Read the column as a serde_json::Value (the with-serde_json-1 feature is already enabled) and re-serialize it for the existing parser. Postgres-compatible engines that hand the plan back as a plain text column are covered by a String fallback.

Testing

  • cargo check passes.
  • Verified Explain Plan now renders against Postgres locally, with and without Analyze.

EXPLAIN (FORMAT JSON) returns the plan in a column typed as json, not
text, so reading it into a String failed with "error deserializing
column 0" and the Explain Plan feature never worked on Postgres.

Read the column as a serde_json::Value and re-serialize it for the
parser, falling back to a plain String for Postgres-compatible engines
that hand the plan back as text.

Fixes #276
@kilo-code-bot
Copy link
Copy Markdown

kilo-code-bot Bot commented Jun 3, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Incremental Review (commit b87b318)

The previous nit about discarding the original json_err in the fallback branch has been addressed — the error is now captured and logged via log::debug! before returning the text-read failure. No new issues introduced.

Files Reviewed (1 file)
  • src-tauri/src/drivers/postgres/explain.rs - No new issues. Previous nit resolved.

Reviewed by kimi-k2.6 · 101,317 tokens

Copy link
Copy Markdown
Contributor

@NewtTheWolf NewtTheWolf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@debba overall this looks good to me 👍 — correct root cause (EXPLAIN (FORMAT JSON) on real PostgreSQL returns a json column, OID 114, not text; I verified this locally against PG 16 with \gdescQUERY PLAN | json). The fix is minimal, the fallback for text-returning PG-compatible engines is a nice touch, and the comment explains the why well. cargo test explain is green and the Explain Plan now renders end-to-end.

Two optional, non-blocking nits inline — points 1 and 2 below. Happy to approve either way.

Comment thread src-tauri/src/drivers/postgres/explain.rs Outdated
// re-serialize. Some Postgres-compatible engines hand back a plain `text`
// column instead, so fall back to reading the raw string in that case.
let plan_json_str = match rows[0].try_get::<_, serde_json::Value>(0) {
Ok(value) => value.to_string(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (non-blocking): this is a triple round-trip — PG sends JSON text → tokio-postgres parses into serde_json::Value.to_string() re-serializes → parse_postgres_json parses it again. Totally fine for small EXPLAIN output, just flagging in case parse_postgres_json could grow a &serde_json::Value entry point to skip the re-parse. Not worth it unless it's trivial.

Copy link
Copy Markdown
Contributor

@NewtTheWolf NewtTheWolf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test

Comment thread src-tauri/src/drivers/postgres/explain.rs Outdated
Co-authored-by: Dominik Spitzli <dominik@spitzli.dev>
@debba debba merged commit 6abe185 into main Jun 4, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants