fix(postgres): read EXPLAIN JSON output as json column#279
Conversation
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
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Incremental Review (commit
|
NewtTheWolf
left a comment
There was a problem hiding this comment.
@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 \gdesc → QUERY 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.
| // 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(), |
There was a problem hiding this comment.
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.
Co-authored-by: Dominik Spitzli <dominik@spitzli.dev>
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 asjson(OID 114), nottext. The code read it straight into aString:tokio-postgresrefuses to deserialize ajsoncolumn into aString, so the call errored out before the plan was ever parsed. This is server-side behaviour that's been stable since theFORMAToption 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(thewith-serde_json-1feature is already enabled) and re-serialize it for the existing parser. Postgres-compatible engines that hand the plan back as a plaintextcolumn are covered by aStringfallback.Testing
cargo checkpasses.