Neatbo.

Inspect PostgreSQL 16 JSON plans

Read one or two PostgreSQL 16 JSON plans, retain full trees, Workers and numeric spelling, explain loops and parallel scope, and compare matching positions.

Browser-local processingInputPostgreSQL 16 EXPLAIN JSONOutputComplete plan report, CSV and originalsUp to 10 MiB per file · File limit: 1

Paste or select the JSON array exported by PostgreSQL 16. Plain text plans need a deliberate manual transcription or JSON export; this tool does not run a query.

Use an existing PostgreSQL 16 EXPLAIN (FORMAT JSON) export. Exporting or analyzing a query remains your database workflow.

Primary plan JSON

Only the selected source for this role is processed. The file is read after its name and combined size pass the checks; inactive sources remain unused.

Ready

No result yet

Before you start

Read one or two PostgreSQL 16 JSON plans, retain full trees, Workers and numeric spelling, explain loops and parallel scope, and compare matching positions.

How to use this tool

  1. Confirm the original export is from PostgreSQL 16 and retain its JSON array.
  2. Select paste or file for the primary; optionally enable comparison and select its source independently.
  3. Read the complete trees, numeric spelling, loops/parallel scope and positional comparison.
  4. Download original plans, envelope, complete report and CSV; copy preserves the full report.

Supported inputs and limits

Primary and comparison raw UTF-8 sources together allow at most 1 MiB. Each filename allows 512 UTF-8 bytes and MIME label 128. Source/metadata budgets are checked before native file reading; originals, role, filename, byte count and SHA256 remain available.

Each plan has 1–32 statements, at most 2048 operators, operator depth 32 and 256 worker records. The version envelope and all source plans together have at most 100000 JSON values and depth 128 with the envelope root at 0; a source-array root is at depth 2.

Only EXPLAIN FORMAT JSON arrays; no text-plan parser or SQL execution. Numeric metrics are finite, nonnegative and lossless. Duplicate keys, BOM, negative zero and unsafe integers refuse.

If any operator-node Actual Startup Time, Actual Total Time, Actual Rows or Actual Loops appears, all four are required. Loop-product time/rows/loops need plain decimal tokens; exponent spelling refuses. Workers retain original fields and validate provided numbers. Partial Actual records such as missing TIMING OFF times currently refuse.

Cost uses planner units. Actual time/rows are per-loop averages; exact decimal string products are reported. Inclusive child work and concurrent participants cannot be summed as wall time; buffers are not multiplied by loops.

Two plans compare only identical statement/child positions, with added/removed/operator-changed flags. No identity or optimization inference. Complete files and copy text total at most 16 MiB.

Choose paste or one JSON file independently for the primary plan and optional comparison. Only enabled roles and selected modes are read. Identical filenames do not change roles. Explicit server version supports PostgreSQL 16 only.

Export TEXT plans separately as FORMAT JSON from PostgreSQL 16. This tool executes no SQL, connects to no database, and cannot establish optimization success or explain all client waiting time. The 2015 TEXT measurements are historical request context; the visible sample uses a saved controlled PostgreSQL 16.15 JSON export.

One absolute 10-second whole deadline starts before metadata/parameter validation and file reading and continues through Worker loading, processing and publication. Cancel, timeout or source change clears old report/table/download artifacts; rerun the same complete sources.

Complete report, CSV, original plans, version envelope and copy text total at most 16 MiB. Reserve capacity before large serialization; typed report/table JSON work separately allows 16 MiB, physical plus typed transport 32 MiB, and cumulative numeric paths 8 MiB. Over-budget work refuses atomically.

Worked example

Example input

[{"Plan":{"Node Type":"Function Scan","Parallel Aware":false,"Async Capable":false,"Function Name":"generate_series","Schema":"pg_catalog","Alias":"a","Startup Cost":0,"Total Cost":0.18,"Plan Rows":1,"Plan Width":4,"Output":["i"],"Function Call":"generate_series(1, 12)","Filter":"((a.i % 2) = 0)","Shared Hit Blocks":0,"Shared Read Blocks":0,"Shared Dirtied Blocks":0,"Shared Written Blocks":0,"Local Hit Blocks":0,"Local Read Blocks":0,"Local Dirtied Blocks":0,"Local Written Blocks":0,"Temp Read Blocks":0,"Temp Written Blocks":0},"Planning":{"Shared Hit Blocks":5,"Shared Read Blocks":0,"Shared Dirtied Blocks":0,"Shared Written Blocks":0,"Local Hit Blocks":0,"Local Read Blocks":0,"Local Dirtied Blocks":0,"Local Written Blocks":0,"Temp Read Blocks":0,"Temp Written Blocks":0}}]
Example options
{"serverMajor":"16","comparisonEnabled":false,"primarySourceMode":"paste"}

Example output

{
  "schema": "neatbo-pg-plan/1",
  "serverMajor": 16,
  "comparisonStrategy": "same-statement-child-position; no identity or optimization inference",
  "costSemantics": "planner cost units, not milliseconds",
  "executionSemantics": "measured database execution; excludes client delivery and rendering; node times inclusive and per-loop",
  "numericLexemes": [
    {
      "pointer": "/serverMajor",
      "lexeme": "16",
      "value": 16
    },
    {
      "pointer": "/plans/0/0/Plan/Startup Cost",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Plan/Total Cost",
      "lexeme": "0.18",
      "value": 0.18
    },
    {
      "pointer": "/plans/0/0/Plan/Plan Rows",
      "lexeme": "1",
      "value": 1
    },
    {
      "pointer": "/plans/0/0/Plan/Plan Width",
      "lexeme": "4",
      "value": 4
    },
    {
      "pointer": "/plans/0/0/Plan/Shared Hit Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Plan/Shared Read Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Plan/Shared Dirtied Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Plan/Shared Written Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Plan/Local Hit Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Plan/Local Read Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Plan/Local Dirtied Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Plan/Local Written Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Plan/Temp Read Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Plan/Temp Written Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Planning/Shared Hit Blocks",
      "lexeme": "5",
      "value": 5
    },
    {
      "pointer": "/plans/0/0/Planning/Shared Read Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Planning/Shared Dirtied Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Planning/Shared Written Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Planning/Local Hit Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Planning/Local Read Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Planning/Local Dirtied Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Planning/Local Written Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Planning/Temp Read Blocks",
      "lexeme": "0",
      "value": 0
    },
    {
      "pointer": "/plans/0/0/Planning/Temp Written Blocks",
      "lexeme": "0",
      "value": 0
    }
  ],
  "plans": [
    {
      "planIndex": 0,
      "statementCount": 1,
      "nodeCount": 1,
      "workerCount": 0,
      "statements": [
        {
          "Plan": {
            "Node Type": "Function Scan",
            "Parallel Aware": false,
            "Async Capable": false,
            "Function Name": "generate_series",
            "Schema": "pg_catalog",
            "Alias": "a",
            "Startup Cost": 0,
            "Total Cost": 0.18,
            "Plan Rows": 1,
            "Plan Width": 4,
            "Output": [
              "i"
            ],
            "Function Call": "generate_series(1, 12)",
            "Filter": "((a.i % 2) = 0)",
            "Shared Hit Blocks": 0,
            "Shared Read Blocks": 0,
            "Shared Dirtied Blocks": 0,
            "Shared Written Blocks": 0,
            "Local Hit Blocks": 0,
            "Local Read Blocks": 0,
            "Local Dirtied Blocks": 0,
            "Local Written Blocks": 0,
            "Temp Read Blocks": 0,
            "Temp Written Blocks": 0
          },
          "Planning": {
            "Shared Hit Blocks": 5,
            "Shared Read Blocks": 0,
            "Shared Dirtied Blocks": 0,
            "Shared Written Blocks": 0,
            "Local Hit Blocks": 0,
            "Local Read Blocks": 0,
            "Local Dirtied Blocks": 0,
            "Local Written Blocks": 0,
            "Temp Read Blocks": 0,
            "Temp Written Blocks": 0
          }
        }
      ],
      "nodes": [
        {
          "index": 0,
          "statementIndex": 0,
          "path": "/plans/0/0/Plan",
          "parent": null,
          "depth": 0,
          "operator": "Function Scan",
          "source": {
            "Node Type": "Function Scan",
            "Parallel Aware": false,
            "Async Capable": false,
            "Function Name": "generate_series",
            "Schema": "pg_catalog",
            "Alias": "a",
            "Startup Cost": 0,
            "Total Cost": 0.18,
            "Plan Rows": 1,
            "Plan Width": 4,
            "Output": [
              "i"
            ],
            "Function Call": "generate_series(1, 12)",
            "Filter": "((a.i % 2) = 0)",
            "Shared Hit Blocks": 0,
            "Shared Read Blocks": 0,
            "Shared Dirtied Blocks": 0,
            "Shared Written Blocks": 0,
            "Local Hit Blocks": 0,
            "Local Read Blocks": 0,
            "Local Dirtied Blocks": 0,
            "Local Written Blocks": 0,
            "Temp Read Blocks": 0,
            "Temp Written Blocks": 0
          },
          "metrics": {
            "Startup Cost": {
              "value": 0,
              "lexeme": "0",
              "unit": "planner-cost-unit"
            },
            "Total Cost": {
              "value": 0.18,
              "lexeme": "0.18",
              "unit": "planner-cost-unit"
            },
            "Plan Rows": {
              "value": 1,
              "lexeme": "1",
              "unit": "count"
            },
            "Plan Width": {
              "value": 4,
              "lexeme": "4",
              "unit": "estimated-bytes-per-row"
            },
            "Shared Hit Blocks": {
              "value": 0,
              "lexeme": "0",
              "unit": "blocks"
            },
            "Shared Read Blocks": {
              "value": 0,
              "lexeme": "0",
              "unit": "blocks"
            },
            "Shared Dirtied Blocks": {
              "value": 0,
              "lexeme": "0",
              "unit": "blocks"
            },
            "Shared Written Blocks": {
              "value": 0,
              "lexeme": "0",
              "unit": "blocks"
            },
            "Local Hit Blocks": {
              "value": 0,
              "lexeme": "0",
              "unit": "blocks"
            },
            "Local Read Blocks": {
              "value": 0,
              "lexeme": "0",
              "unit": "blocks"
            },
            "Local Dirtied Blocks": {
              "value": 0,
              "lexeme": "0",
              "unit": "blocks"
            },
            "Local Written Blocks": {
              "value": 0,
              "lexeme": "0",
              "unit": "blocks"
            },
            "Temp Read Blocks": {
              "value": 0,
              "lexeme": "0",
              "unit": "blocks"
            },
            "Temp Written Blocks": {
              "value": 0,
              "lexeme": "0",
              "unit": "blocks"
            }
          },
          "actualPresent": false,
          "parallelScope": false,
          "inclusiveAverageTimeMs": null,
          "timeAcrossLoopsMs": null,
          "rowsAcrossLoops": null,
          "timeSemantics": "inclusive-per-loop; children-overlap; not-additive",
          "buffersSemantics": "reported-inclusive-node-counts; not-multiplied-by-loops"
        }
      ]
    }
  ],
  "comparisons": [],
  "sourceInputs": [
    {
      "role": "primary",
      "kind": "paste",
      "name": "primary-pasted.json",
      "mime": "application/json",
      "bytes": 783,
      "sha256": "26a4039336d776def17a9ad9cab3e8bcc63d7dd992378a72e12d92b09357b9da"
    }
  ],
  "inputBytes": 783,
  "sourceCount": 1,
  "operatorCount": 1,
  "statementCount": 1,
  "comparisonCount": 0
}

When something does not work

Export text plans as JSON; confirm version and missing Actual fields. Split statements for budget failures. Keep originals, correct the source and retry; no partial plans are returned.

Frequently asked questions

Why not add all node times?

A parent includes child work and parallel participants may overlap. Adding them can double-count the same work.

Does the plan explain all 30 seconds observed by a client?

No. Database execution measurements exclude delivery and client rendering; this distinction does not establish that database optimization is unnecessary.

Documentation & further reading

Related tools