Skip to content

Commit a3feec0

Browse files
committed
add optimzer rule
1 parent 4fec751 commit a3feec0

2 files changed

Lines changed: 87 additions & 0 deletions

File tree

datafusion/optimizer/src/reorder_join/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@
2020
pub mod cost;
2121
pub mod join_graph;
2222
pub mod left_deep_join_plan;
23+
pub mod rule;
24+
25+
pub use rule::ReorderJoinRule;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
//! `OptimizerRule` wrapper for join reordering.
19+
//!
20+
//! Append this to an `Optimizer`'s rule list (or to a
21+
//! `SessionStateBuilder` via `with_optimizer_rule`) so the IK84 reorder
22+
//! runs *after* `ExtractEquijoinPredicate` has lifted equi-conditions
23+
//! into the joins' `on` clauses. Running it before that point leaves the
24+
//! reorder with empty-`on` cross-products and a disconnected join graph.
25+
26+
use std::sync::Arc;
27+
28+
use datafusion_common::{Result, tree_node::Transformed};
29+
use datafusion_expr::LogicalPlan;
30+
31+
use crate::{OptimizerConfig, OptimizerRule};
32+
33+
use super::{
34+
cost::{DefaultCostEstimator, JoinCostEstimator},
35+
left_deep_join_plan::optimal_left_deep_join_plan,
36+
};
37+
38+
/// Optimizer-rule wrapper around [`optimal_left_deep_join_plan`].
39+
#[derive(Debug)]
40+
pub struct ReorderJoinRule {
41+
estimator: Arc<dyn JoinCostEstimator + Send + Sync>,
42+
}
43+
44+
impl ReorderJoinRule {
45+
pub fn new(estimator: Arc<dyn JoinCostEstimator + Send + Sync>) -> Self {
46+
Self { estimator }
47+
}
48+
}
49+
50+
impl Default for ReorderJoinRule {
51+
fn default() -> Self {
52+
Self::new(Arc::new(DefaultCostEstimator))
53+
}
54+
}
55+
56+
impl OptimizerRule for ReorderJoinRule {
57+
fn name(&self) -> &str {
58+
"reorder_join"
59+
}
60+
61+
// `optimal_left_deep_join_plan` does its own top-level traversal and
62+
// short-circuits when the plan has no joins, so we don't want the
63+
// framework to walk the tree on our behalf.
64+
fn apply_order(&self) -> Option<crate::optimizer::ApplyOrder> {
65+
None
66+
}
67+
68+
fn rewrite(
69+
&self,
70+
plan: LogicalPlan,
71+
_config: &dyn OptimizerConfig,
72+
) -> Result<Transformed<LogicalPlan>> {
73+
let before = plan.clone();
74+
let after = optimal_left_deep_join_plan(plan, self.estimator.as_ref())?;
75+
// IK84 is deterministic on a stable graph, so a second pass over
76+
// an already-optimal plan reproduces the same chain and we
77+
// converge by reporting no change.
78+
if after == before {
79+
Ok(Transformed::no(after))
80+
} else {
81+
Ok(Transformed::yes(after))
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)