Skip to content

feat(explain): plan trees and the most expensive node for PostgreSQL, MySQL and SQLite (#1164) - #1227

Merged
ZhuchkaTriplesix merged 3 commits into
devfrom
feat/1164-visual-plan
Oct 9, 2026
Merged

ZhuchkaTriplesix merged 3 commits into
devfrom
feat/1164-visual-plan

Conversation

@ZhuchkaTriplesix

Copy link
Copy Markdown
Member

Refs #1164

What was wrong

Explain showed the plan as text lines; finding the expensive node in a nested plan meant reading indentation.

Changes

  • query_plan.dart: a PlanNode tree and parsers for PostgreSQL EXPLAIN (FORMAT JSON), MySQL EXPLAIN FORMAT=JSON (nested loop of tables) and SQLite EXPLAIN QUERY PLAN rows. Each node has its own cost (cumulative cost minus its children's); the node with the largest own cost is highlighted (QueryPlanParser.hottest).
  • Each driver implements explainTree; the base delegate returns none, so a driver without one keeps the text only.
  • The result area of an explained query shows a Plan | Text switch. Plan draws the tree top-down with the hottest node outlined and each node's share of the cost as a bar. Text is the previous text plan.
  • Tests on recorded plans: a join of three tables gives the right parents and children; rows and costs come through; the own cost subtracts the children; the hottest node is the scan with the largest own cost; SQLite gives no hottest node; MySQL tables become children of the query block.

Not in this PR

  • The "click a node for the details panel" part of the issue: nodes show their operation, relation, rows and cost bar, but the other fields are not shown yet (they are parsed into PlanNode.details).
  • EXPLAIN ANALYZE and its buffers (the issue asks for it behind an explicit action): not added.
  • The acceptance test that a join of three real tables renders with the right structure needs a database; the parser tests cover the structure from recorded plans.

Not verified locally

Tests were not run locally, per the project rule.

… MySQL and SQLite (#1164)

Explain showed the plan as text lines, and finding the expensive node meant
reading indentation. Parsers build a tree of operations from the JSON plans of
PostgreSQL and MySQL and from the rows of SQLite's EXPLAIN QUERY PLAN. Each
node has its own cost (its cumulative cost minus its children's); the node with
the largest own cost is the one to highlight. Tests use recorded plans.
Each driver gives the plan as a tree: PostgreSQL EXPLAIN (FORMAT JSON), MySQL
EXPLAIN FORMAT=JSON, SQLite EXPLAIN QUERY PLAN rows. The result area of an
explained query shows the tree with the most expensive node highlighted and each
node's share of the cost as a bar, and a Plan | Text switch keeps the text plan.
A driver without a tree, or a plan that cannot be read, keeps the text only.
@github-actions github-actions Bot added enhancement New feature or request editor Code/SQL editor ui User interface components and widgets P3 Low priority / Polish & Enhancements labels Oct 9, 2026
…gives no tree (#1164)

MySQL's prefix_cost is cumulative along the nested loop, so each table now keeps
its own part, and the query block's cost is the sum of its tables'. A plan text
that is not JSON gives no tree instead of throwing.
@ZhuchkaTriplesix
ZhuchkaTriplesix merged commit ae9b74b into dev Oct 9, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

editor Code/SQL editor enhancement New feature or request P3 Low priority / Polish & Enhancements ui User interface components and widgets

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant