Class PhysicalPlanPrinter

java.lang.Object
com.darkcollective.relix.plan.internal.PhysicalPlanPrinter

public final class PhysicalPlanPrinter extends Object
Renders a PhysicalNode plan as an ASCII tree for --explain.

The output focuses on the physical decisions the planner made — the choices that are invisible in the logical IR report: which sub-trees were pushed to the database as a single PhysicalNode.PushedScan (with the generated native query), and each join's algorithm and build side. Streaming unary operators (selection, projection, rename, …) are shown by name only; their detail already appears in the IR report.

Tree drawing matches the IR report style: └─ for the last child, ├─ for the others, and │ for continuation.

When PlanEstimates are supplied, each line ends with the row count the planner estimated for that node ( ~1200 rows), or ~? rows where it had none. That is what makes a cardinality estimate — and so the build-side and merge-versus-hash decisions it drives — auditable from outside the cost model.

Naming convention

This view names operators; it does not restate the algebra, so it uses no operator glyphs of its own. The rule, relative to the IR report's label for the same operator:

  • Where the IR uses a glyph (σ, π, γ, ⋈, ∀, …), this view uses a Titlecase word — Select, Project, Aggregate, Join, Universal. A glyph would read worse here, because a physical Select carries no predicate to go with it.
  • Where the IR uses an uppercase keyword (an operator with no glyph in the language — CLOSURE, TRACE, TOP, SESSIONIZE, COVER, OPTIMIZE, …), this view uses the same keyword, so the two views agree exactly.
  • Connective keywords inside a label are uppercase either way (PER, AS, BY, VIA, SORT, OVER, SEED, OFFSET).
  • Physical annotations — the information only this view has — are lowercase or bracketed: streaming, /HASH build=LEFT, [dijkstra], [constructive].

Sub-expressions shared with the IR report (window and solver clauses, sort keys, generator bounds) come from DisplayLabels and are byte-identical in both views.

  • Method Details

    • explain

      public static String explain(PhysicalNode root)
      Renders root and its descendants as a multi-line ASCII tree, without cardinality estimates.
      Parameters:
      root - the physical plan root; must not be null
      Returns:
      the rendered tree, newline-terminated per line
    • explain

      public static String explain(PhysicalNode root, PlanEstimates estimates)
      Renders root and its descendants as a multi-line ASCII tree, annotating each node with the row count the planner estimated for it.

      The annotation is appended as ~N rows, and a node the planner did not cost prints ~? rows — not ~0 rows. The distinction is the point: an estimate of zero is a claim about the data, and "nobody costed this" is not. The whole column is omitted when no node has an estimate at all, so a plan rendered outside a planning run looks exactly as it did before this existed.

      Parameters:
      root - the physical plan root; must not be null
      estimates - the estimates recorded while planning; must not be null (use PlanEstimates.none() for none)
      Returns:
      the rendered tree, newline-terminated per line