Skip to main content

Module reorder

Module reorder 

Source
Expand description

v6.2.3 — JOIN reorder planner pass.

Runs after parse + clock rewrite + ORDER BY position resolution. For SelectStatements with multiple INNER-joined tables, picks an ordering that minimises the cumulative nested-loop work — leveraging v6.2.0’s Statistics for per-edge selectivity estimates.

Algorithm:

  • Identify tables: from.primary + from.joins[*].
  • Identify edges: each INNER JOIN ... ON <expr> adds one edge whose endpoints are the table names referenced by the ON expression (extracted by walking ColumnName nodes). LEFT / CROSS joins disable reorder — they have semantics-preserving order constraints we don’t unpack in v6.2.3.
  • Enumerate orderings:
    • For n ≤ 4 tables: brute force all n! orderings.
    • For n > 4: greedy — pick the smallest table first, then at each step pick the next table that gives the smallest expected output size given the edges already applicable.
  • Cost an ordering: walk left-to-right tracking the running output size. Every edge becomes applicable as soon as both its endpoint tables are in the prefix; multiplying the running size by selectivity::equal(stats, …) / n_distinct for that edge updates the size. Step cost = running_size × new_table_size. Total cost = sum of step costs.
  • Pick the minimum-cost ordering and rewrite from.primary + from.joins in that order. ON predicates travel with their edges — they re-attach to whichever join introduces both endpoint tables.

All of v6.2.3 ships pure-AST rewriting. The executor at exec_joined_select doesn’t change shape — it consumes the newly-ordered FROM clause unchanged.

Constants§

FULL_ENUM_MAX
v6.2.3 — full-enumeration cap. v6.2.x can re-tune; the value determines n! plan-space size (4! = 24, 5! = 120, 6! = 720 — 6 is on the verge of “noticeable” in micro-bench terms).

Statics§

REORDER_INNER_RUN_FIRED
REORDER_INNER_RUN_TRIED
v7.37.9 Phase 0 diagnostic counters — see .claude/notes/v7.37.9-class-a-c-cascade-closure-plan.md. These are read-only telemetry, do not gate any code path.

Functions§

choose_order_for_test
v6.2.3 — entry point. Rewrites stmt.from (when present) so the join chain is in cost-minimising order. No-op when:
reorder_joins
reorder_joins_with
v7.38 元机制 D acceptor — SPG_TEST_PLAN_DETERMINISTIC=1 makes cost-based join reorder a no-op so regression tests that pin “same SQL → same plan order” don’t drift when statistics shift. Production reads call reorder_joins(deterministic=false); the engine’s gate is crates/spg-engine/src/execute.rs::Engine::env_cfg().plan_deterministic.