java.lang.Object
com.darkcollective.relix.optimizer.internal.JoinRulesPass

public final class JoinRulesPass extends Object
Optimization pass that converts Cartesian products with selections into theta joins, and pushes one-sided selections into join inputs (JOIN-001 and JOIN-002).
  • JOIN-001 — Converts σ(p)(A × B) into a theta join A ⨝_p B when the predicate p references columns from both inputs. The selection node is absorbed into the join condition, allowing the executor to filter during the join rather than materialising the full cross product first.
  • JOIN-002 — Pushes a selection into the appropriate input of a join when its predicate references only one side. Applies to ProductNode (the selection is pushed into that product input, and the product is preserved) and ThetaJoinNode (the selection is pushed into the matching join input). This rule fires after JOIN-001 has been attempted, so it handles the one-sided residuals that cannot be converted to join conditions.

Traversal strategy

The traversal is top-down for selection nodes: the rules are attempted before recursing into the input. If a rule fires, the pass recurses into the rewritten tree. If no rule fires at the top, the pass recurses into the input; if that changes the input, the rules are retried on the updated selection. This two-step retry allows the common pattern σ(join_cond)(σ(filter)(A × B)) to be fully optimised in a single pass: JOIN-002 pushes the inner filter into the left product input, and JOIN-001 then converts the outer selection to a theta join.

Schema annotations on product and join inputs are used to determine which side of a binary operator each predicate attribute belongs to. To handle the case where a prior transformation wrapped an input in a SelectionNode (eliminating its direct annotation), the helper schemaOf(RelNode, SchemaAnnotations) looks through intervening selection nodes to find the nearest annotated ancestor; this is safe because selections never change the column schema of their input.

This pass should run after SelectionPushdownPass (which handles selections over existing joins) and before the cleanup passes.

This class is package-private and stateless; call apply(RelNode, String, SchemaAnnotations, OptimizationContext) as a static method.