All Known Implementing Classes:
PassRule

public interface OptimizationRule
A single query-optimization transformation rule.

Each rule encapsulates one rewrite family (identified by its code(), or by codes() for a rule that emits several related codes) and is applied to a RelNode tree by QueryOptimizer. The rules that make up the default pipeline, the order they run in, and which of them are iterated to a fixpoint are declared by OptimizationPipeline. Rules must be:

  • Idempotent — applying the same rule twice to an already-optimized tree returns the same result as applying it once.
  • Semantics-preserving — the rewritten tree must produce identical results to the original for any legal input.
  • Stateless (recommended) — implementations should carry no mutable state so they are safe to share across multiple optimization runs.

Contract for apply(com.darkcollective.relix.ast.RelNode, java.lang.String, com.darkcollective.relix.semantic.SchemaAnnotations, com.darkcollective.relix.optimizer.internal.OptimizationContext)

  1. Return the original node unchanged when the rule does not apply to this node. Do not call OptimizationContext.record(com.darkcollective.relix.optimizer.OptimizationCode, java.lang.String, java.lang.String, com.darkcollective.relix.ast.SourceLocation) in this case.
  2. Return a rewritten RelNode when the rule fires, and call ctx.record() exactly once per top-level rewrite performed at this node.
  3. Most rules only inspect the current node and rely on the QueryOptimizer engine to handle bottom-up or top-down traversal. Rules that need to recurse into children explicitly should document this clearly.

The two obligations above are what OptimizationPipeline's fixpoint driver reads as "progress". It re-runs an iterated phase only while its rules both recorded something and returned a different tree (reference inequality, per RelNode.mapChildren(java.util.function.UnaryOperator<com.darkcollective.relix.ast.RelNode>)'s identity contract). A rule that rewrites without recording, or records without rewriting, is not a correctness bug on its own but does stop the phase early.

  • Method Details

    • code

      Returns the OptimizationCode that identifies this rule.

      For a rule that emits several codes this is the primary one — the first entry of codes().

      Returns:
      the code; never null
    • codes

      default List<OptimizationCode> codes()
      Returns every OptimizationCode this rule can emit, primary first.

      Rules are registered one per pass, and several passes cover a family of related rewrites — SelectionPushdownPass alone emits SEL-003..009. The default implementation returns just code(), which is right for a single-code rule.

      Returns:
      unmodifiable list, never null or empty
    • name

      default String name()
      Returns a short stable identifier for this rule, used when a pipeline is rendered or a rule is named in a diagnostic.

      Defaults to the primary code()'s code string.

      Returns:
      the name; never null or blank
    • apply

      RelNode apply(RelNode node, String queryName, SchemaAnnotations schemas, OptimizationContext ctx)
      Applies this rule to the given node, returning either the original node (if the rule did not fire) or a rewritten node (if it did).
      Parameters:
      node - the node to inspect and possibly rewrite; never null
      queryName - display name of the relation/query being optimized, used in TransformationRecords; never blank
      schemas - schema annotations from semantic analysis; rules that need to know a node's output schema use this to look it up; never null
      ctx - mutable context that accumulates transformation records; never null
      Returns:
      the original node when the rule did not fire, or a new RelNode representing the rewritten tree; never null