risingwave_frontend/optimizer/plan_node/
batch_nested_loop_join.rs1use pretty_xmlish::{Pretty, XmlNode};
16use risingwave_pb::batch_plan::NestedLoopJoinNode;
17use risingwave_pb::batch_plan::plan_node::NodeBody;
18
19use super::batch::prelude::*;
20use super::utils::{Distill, childless_record};
21use super::{
22 BatchPlanRef as PlanRef, ExprRewritable, PlanBase, PlanTreeNodeBinary, ToBatchPb,
23 ToDistributedBatch, generic,
24};
25use crate::error::Result;
26use crate::expr::{Expr, ExprImpl, ExprRewriter, ExprVisitor};
27use crate::optimizer::plan_node::ToLocalBatch;
28use crate::optimizer::plan_node::expr_visitable::ExprVisitable;
29use crate::optimizer::plan_node::utils::IndicesDisplay;
30use crate::optimizer::property::{Distribution, Order, RequiredDist};
31use crate::utils::ConditionDisplay;
32
33#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub struct BatchNestedLoopJoin {
37 pub base: PlanBase<Batch>,
38 core: generic::Join<PlanRef>,
39}
40
41impl BatchNestedLoopJoin {
42 pub fn new(core: generic::Join<PlanRef>) -> Self {
43 let dist = Self::derive_dist(core.left.distribution(), core.right.distribution());
44 let base = PlanBase::new_batch_with_core(&core, dist, Order::any());
45 Self { base, core }
46 }
47
48 fn derive_dist(left: &Distribution, right: &Distribution) -> Distribution {
49 match (left, right) {
50 (Distribution::Single, Distribution::Single) => Distribution::Single,
51 (_, _) => unreachable!("{}{}", left, right),
52 }
53 }
54}
55
56impl Distill for BatchNestedLoopJoin {
57 fn distill<'a>(&self) -> XmlNode<'a> {
58 let verbose = self.base.ctx().is_explain_verbose();
59 let mut vec = Vec::with_capacity(if verbose { 3 } else { 2 });
60 vec.push(("type", Pretty::debug(&self.core.join_type)));
61
62 let concat_schema = self.core.concat_schema();
63 vec.push((
64 "predicate",
65 Pretty::debug(&ConditionDisplay {
66 condition: &self.core.on,
67 input_schema: &concat_schema,
68 }),
69 ));
70
71 if verbose {
72 let data = IndicesDisplay::from_join(&self.core, &concat_schema);
73 vec.push(("output", data));
74 }
75
76 childless_record("BatchNestedLoopJoin", vec)
77 }
78}
79
80impl PlanTreeNodeBinary<Batch> for BatchNestedLoopJoin {
81 fn left(&self) -> PlanRef {
82 self.core.left.clone()
83 }
84
85 fn right(&self) -> PlanRef {
86 self.core.right.clone()
87 }
88
89 fn clone_with_left_right(&self, left: PlanRef, right: PlanRef) -> Self {
90 let mut core = self.core.clone();
91 core.left = left;
92 core.right = right;
93 Self::new(core)
94 }
95}
96
97impl_plan_tree_node_for_binary! { Batch, BatchNestedLoopJoin }
98
99impl ToDistributedBatch for BatchNestedLoopJoin {
100 fn to_distributed(&self) -> Result<PlanRef> {
101 let left = self
102 .left()
103 .to_distributed_with_required(&Order::any(), &RequiredDist::single())?;
104 let right = self
105 .right()
106 .to_distributed_with_required(&Order::any(), &RequiredDist::single())?;
107
108 Ok(self.clone_with_left_right(left, right).into())
109 }
110}
111
112impl ToBatchPb for BatchNestedLoopJoin {
113 fn to_batch_prost_body(&self) -> NodeBody {
114 NodeBody::NestedLoopJoin(NestedLoopJoinNode {
115 join_type: self.core.join_type as i32,
116 join_cond: Some(ExprImpl::from(self.core.on.clone()).to_expr_proto()),
117 output_indices: self.core.output_indices.iter().map(|&x| x as u32).collect(),
118 })
119 }
120}
121
122impl ToLocalBatch for BatchNestedLoopJoin {
123 fn to_local(&self) -> Result<PlanRef> {
124 let left = RequiredDist::single()
125 .batch_enforce_if_not_satisfies(self.left().to_local()?, &Order::any())?;
126
127 let right = RequiredDist::single()
128 .batch_enforce_if_not_satisfies(self.right().to_local()?, &Order::any())?;
129
130 Ok(self.clone_with_left_right(left, right).into())
131 }
132}
133
134impl ExprRewritable<Batch> for BatchNestedLoopJoin {
135 fn has_rewritable_expr(&self) -> bool {
136 true
137 }
138
139 fn rewrite_exprs(&self, r: &mut dyn ExprRewriter) -> PlanRef {
140 let mut core = self.core.clone();
141 core.rewrite_exprs(r);
142 Self::new(core).into()
143 }
144}
145
146impl ExprVisitable for BatchNestedLoopJoin {
147 fn visit_exprs(&self, v: &mut dyn ExprVisitor) {
148 self.core.visit_exprs(v);
149 }
150}