Skip to main content

risingwave_frontend/optimizer/plan_node/
mod.rs

1// Copyright 2022 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Defines all kinds of node in the plan tree, each node represent a relational expression.
16//!
17//! We use a immutable style tree structure, every Node are immutable and cannot be modified after
18//! it has been created. If you want to modify the node, such as rewriting the expression in a
19//! `ProjectNode` or changing a node's input node, you need to create a new node. We use Rc as the
20//! node's reference, and a node just storage its inputs' reference, so change a node just need
21//! create one new node but not the entire sub-tree.
22//!
23//! So when you want to add a new node, make sure:
24//! - each field in the node struct are private
25//! - recommend to implement the construction of Node in a unified `new()` function, if have multi
26//!   methods to construct, make they have a consistent behavior
27//! - all field should be valued in construction, so the properties' derivation should be finished
28//!   in the `new()` function.
29
30use std::collections::{HashMap, HashSet};
31use std::fmt::Debug;
32use std::hash::Hash;
33use std::marker::PhantomData;
34use std::ops::Deref;
35use std::rc::Rc;
36
37use downcast_rs::{Downcast, impl_downcast};
38use dyn_clone::DynClone;
39use itertools::Itertools;
40use paste::paste;
41use petgraph::dot::{Config, Dot};
42use petgraph::graph::Graph;
43use pretty_xmlish::{Pretty, PrettyConfig};
44use risingwave_common::catalog::Schema;
45use risingwave_common::util::recursive::{self, Recurse};
46use risingwave_pb::batch_plan::PlanNode as PbBatchPlan;
47use risingwave_pb::stream_plan::StreamNode as PbStreamPlan;
48use serde::Serialize;
49
50use self::batch::BatchPlanNodeMetadata;
51use self::generic::{GenericPlanRef, PhysicalPlanRef};
52use self::stream::StreamPlanNodeMetadata;
53use self::utils::Distill;
54use super::property::{
55    Distribution, FunctionalDependencySet, MonotonicityMap, Order, WatermarkColumns,
56};
57use crate::error::{ErrorCode, Result};
58use crate::optimizer::property::StreamKind;
59use crate::optimizer::{PlanVisitor, ShareId};
60use crate::session::current::notice_to_user;
61use crate::utils::{PrettySerde, build_graph_from_pretty};
62
63/// A marker trait for different conventions, used for enforcing type safety.
64///
65/// Implementors are [`Logical`], [`Batch`], and [`Stream`].
66pub trait ConventionMarker: 'static + Sized + Clone + Debug + Eq + PartialEq + Hash {
67    /// The extra fields in the [`PlanBase`] of this convention.
68    type Extra: 'static + Eq + Hash + Clone + Debug;
69    type ShareNode: ShareNode<Self>;
70    type PlanRefDyn: PlanNodeCommon<Self> + Eq + Hash + ?Sized;
71    type PlanNodeType;
72
73    fn as_share(plan: &Self::PlanRefDyn) -> Option<&Self::ShareNode>;
74}
75
76pub trait ShareNode<C: ConventionMarker>:
77    AnyPlanNodeMeta<C> + PlanTreeNodeUnary<C> + 'static
78{
79    fn share_id(&self) -> ShareId;
80    fn new_share(share: generic::Share<PlanRef<C>>) -> PlanRef<C>;
81    fn replace_input(&self, plan: PlanRef<C>) -> PlanRef<C>;
82    fn fork_with_input(&self, plan: PlanRef<C>) -> PlanRef<C>;
83}
84
85pub struct NoShareNode<C: ConventionMarker>(!, PhantomData<C>);
86
87impl<C: ConventionMarker> ShareNode<C> for NoShareNode<C> {
88    fn share_id(&self) -> ShareId {
89        unreachable!()
90    }
91
92    fn new_share(_plan: generic::Share<PlanRef<C>>) -> PlanRef<C> {
93        unreachable!()
94    }
95
96    fn replace_input(&self, _plan: PlanRef<C>) -> PlanRef<C> {
97        unreachable!()
98    }
99
100    fn fork_with_input(&self, _plan: PlanRef<C>) -> PlanRef<C> {
101        unreachable!()
102    }
103}
104
105impl<C: ConventionMarker> PlanTreeNodeUnary<C> for NoShareNode<C> {
106    fn input(&self) -> PlanRef<C> {
107        unreachable!()
108    }
109
110    fn clone_with_input(&self, _input: PlanRef<C>) -> Self {
111        unreachable!()
112    }
113}
114
115impl<C: ConventionMarker> AnyPlanNodeMeta<C> for NoShareNode<C> {
116    fn node_type(&self) -> C::PlanNodeType {
117        unreachable!()
118    }
119
120    fn plan_base(&self) -> &PlanBase<C> {
121        unreachable!()
122    }
123}
124
125/// The marker for logical convention.
126#[derive(Clone, Debug, Eq, PartialEq, Hash)]
127pub struct Logical;
128impl ConventionMarker for Logical {
129    type Extra = plan_base::NoExtra;
130    type PlanNodeType = LogicalPlanNodeType;
131    type PlanRefDyn = dyn LogicalPlanNode;
132    type ShareNode = LogicalShare;
133
134    fn as_share(plan: &Self::PlanRefDyn) -> Option<&Self::ShareNode> {
135        plan.as_logical_share()
136    }
137}
138
139/// The marker for batch convention.
140#[derive(Clone, Debug, Eq, PartialEq, Hash)]
141pub struct Batch;
142impl ConventionMarker for Batch {
143    type Extra = plan_base::BatchExtra;
144    type PlanNodeType = BatchPlanNodeType;
145    type PlanRefDyn = dyn BatchPlanNode;
146    type ShareNode = NoShareNode<Batch>;
147
148    fn as_share(_plan: &Self::PlanRefDyn) -> Option<&Self::ShareNode> {
149        None
150    }
151}
152
153/// The marker for stream convention.
154#[derive(Clone, Debug, Eq, PartialEq, Hash)]
155pub struct Stream;
156impl ConventionMarker for Stream {
157    type Extra = plan_base::StreamExtra;
158    type PlanNodeType = StreamPlanNodeType;
159    type PlanRefDyn = dyn StreamPlanNode;
160    type ShareNode = StreamShare;
161
162    fn as_share(plan: &Self::PlanRefDyn) -> Option<&Self::ShareNode> {
163        plan.as_stream_share()
164    }
165}
166
167/// The trait for accessing the meta data and [`PlanBase`] for plan nodes.
168pub trait PlanNodeMeta {
169    type Convention: ConventionMarker;
170    const NODE_TYPE: <Self::Convention as ConventionMarker>::PlanNodeType;
171    /// Get the reference to the [`PlanBase`] with corresponding convention.
172    fn plan_base(&self) -> &PlanBase<Self::Convention>;
173}
174
175// Intentionally made private.
176mod plan_node_meta {
177    use super::*;
178
179    /// The object-safe version of [`PlanNodeMeta`], used as a super trait of `PlanNode`.
180    ///
181    /// Check [`PlanNodeMeta`] for more details.
182    pub trait AnyPlanNodeMeta<C: ConventionMarker> {
183        fn node_type(&self) -> C::PlanNodeType;
184        fn plan_base(&self) -> &PlanBase<C>;
185    }
186
187    /// Implement [`AnyPlanNodeMeta`] for all [`PlanNodeMeta`].
188    impl<P> AnyPlanNodeMeta<P::Convention> for P
189    where
190        P: PlanNodeMeta,
191    {
192        fn node_type(&self) -> <P::Convention as ConventionMarker>::PlanNodeType {
193            P::NODE_TYPE
194        }
195
196        fn plan_base(&self) -> &PlanBase<P::Convention> {
197            <Self as PlanNodeMeta>::plan_base(self)
198        }
199    }
200}
201use plan_node_meta::AnyPlanNodeMeta;
202
203pub trait PlanNodeCommon<C: ConventionMarker> = PlanTreeNode<C>
204    + DynClone
205    + DynEq
206    + DynHash
207    + Distill
208    + Debug
209    + Downcast
210    + ExprRewritable<C>
211    + ExprVisitable
212    + AnyPlanNodeMeta<C>;
213
214/// The common trait over all plan nodes. Used by optimizer framework which will treat all node as
215/// `dyn PlanNode`
216///
217/// We split the trait into lots of sub-trait so that we can easily use macro to impl them.
218pub trait StreamPlanNode: PlanNodeCommon<Stream> + TryToStreamPb {}
219pub trait BatchPlanNode:
220    PlanNodeCommon<Batch> + ToDistributedBatch + ToLocalBatch + TryToBatchPb
221{
222}
223pub trait LogicalPlanNode:
224    PlanNodeCommon<Logical> + ColPrunable + PredicatePushdown + ToBatch + ToStream
225{
226}
227
228macro_rules! impl_trait {
229    ($($convention:ident),+) => {
230        paste! {
231            $(
232                impl Hash for dyn [<$convention  PlanNode>] {
233                    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
234                        self.dyn_hash(state);
235                    }
236                }
237
238                impl PartialEq for dyn [<$convention  PlanNode>] {
239                    fn eq(&self, other: &Self) -> bool {
240                        self.dyn_eq(other.as_dyn_eq())
241                    }
242                }
243
244                impl Eq for dyn [<$convention  PlanNode>] {}
245            )+
246        }
247    };
248}
249
250impl_trait!(Batch, Stream, Logical);
251impl_downcast!(BatchPlanNode);
252impl_downcast!(LogicalPlanNode);
253impl_downcast!(StreamPlanNode);
254
255// Using a new type wrapper allows direct function implementation on `PlanRef`,
256// and we currently need a manual implementation of `PartialEq` for `PlanRef`.
257#[expect(clippy::derived_hash_with_manual_eq)]
258#[derive(Debug, Eq, Hash)]
259pub struct PlanRef<C: ConventionMarker>(Rc<C::PlanRefDyn>);
260
261impl<C: ConventionMarker> Clone for PlanRef<C> {
262    fn clone(&self) -> Self {
263        Self(self.0.clone())
264    }
265}
266
267pub type LogicalPlanRef = PlanRef<Logical>;
268pub type StreamPlanRef = PlanRef<Stream>;
269pub type BatchPlanRef = PlanRef<Batch>;
270
271// Cannot use the derived implementation for now.
272// See https://github.com/rust-lang/rust/issues/31740
273#[expect(clippy::op_ref)]
274impl<C: ConventionMarker> PartialEq for PlanRef<C> {
275    fn eq(&self, other: &Self) -> bool {
276        &self.0 == &other.0
277    }
278}
279
280impl<C: ConventionMarker> Deref for PlanRef<C> {
281    type Target = C::PlanRefDyn;
282
283    fn deref(&self) -> &Self::Target {
284        self.0.deref()
285    }
286}
287
288impl<T: LogicalPlanNode> From<T> for PlanRef<Logical> {
289    fn from(value: T) -> Self {
290        PlanRef(Rc::new(value) as _)
291    }
292}
293
294impl<T: StreamPlanNode> From<T> for PlanRef<Stream> {
295    fn from(value: T) -> Self {
296        PlanRef(Rc::new(value) as _)
297    }
298}
299
300impl<T: BatchPlanNode> From<T> for PlanRef<Batch> {
301    fn from(value: T) -> Self {
302        PlanRef(Rc::new(value) as _)
303    }
304}
305
306impl<C: ConventionMarker> Layer for PlanRef<C> {
307    type Sub = Self;
308
309    fn map<F>(self, f: F) -> Self
310    where
311        F: FnMut(Self::Sub) -> Self::Sub,
312    {
313        self.clone_root_with_inputs(&self.inputs().into_iter().map(f).collect_vec())
314    }
315
316    fn descent<F>(&self, f: F)
317    where
318        F: FnMut(&Self::Sub),
319    {
320        self.inputs().iter().for_each(f);
321    }
322}
323
324#[derive(Clone, Debug, Copy, Serialize, Hash, Eq, PartialEq, PartialOrd, Ord)]
325pub struct PlanNodeId(pub i32);
326
327impl PlanNodeId {
328    pub fn to_stream_node_operator_id(self) -> StreamNodeLocalOperatorId {
329        StreamNodeLocalOperatorId::new(self.0 as _)
330    }
331}
332
333/// A more sophisticated `Endo` taking into account of the DAG structure of `PlanRef`.
334/// In addition to `Endo`, one have to specify the `cached` function
335/// to persist transformed `LogicalShare` and their results,
336/// and the `dag_apply` function will take care to only transform every `LogicalShare` nodes once.
337///
338/// Note: Due to the way super trait is designed in rust,
339/// one need to have separate implementation blocks of `Endo<PlanRef>` and `EndoPlan`.
340/// And conventionally the real transformation `apply` is under `Endo<PlanRef>`,
341/// although one can refer to `dag_apply` in the implementation of `apply`.
342pub trait EndoPlan: Endo<LogicalPlanRef> {
343    // Return the cached result of `plan` if present,
344    // otherwise store and return the value provided by `f`.
345    // Notice that to allow mutable access of `self` in `f`,
346    // we let `f` to take `&mut Self` as its first argument.
347    fn cached<F>(&mut self, plan: LogicalPlanRef, f: F) -> LogicalPlanRef
348    where
349        F: FnMut(&mut Self) -> LogicalPlanRef;
350
351    fn dag_apply(&mut self, plan: LogicalPlanRef) -> LogicalPlanRef {
352        match plan.as_logical_share() {
353            Some(_) => self.cached(plan.clone(), |this| this.tree_apply(plan.clone())),
354            None => self.tree_apply(plan),
355        }
356    }
357}
358
359/// A more sophisticated `Visit` taking into account of the DAG structure of `PlanRef`.
360/// In addition to `Visit`, one have to specify `visited`
361/// to store and report visited `LogicalShare` nodes,
362/// and the `dag_visit` function will take care to only visit every `LogicalShare` nodes once.
363/// See also `EndoPlan`.
364pub trait VisitPlan: Visit<LogicalPlanRef> {
365    // Skip visiting `plan` if visited, otherwise run the traversal provided by `f`.
366    // Notice that to allow mutable access of `self` in `f`,
367    // we let `f` to take `&mut Self` as its first argument.
368    fn visited<F>(&mut self, plan: &LogicalPlanRef, f: F)
369    where
370        F: FnMut(&mut Self);
371
372    fn dag_visit(&mut self, plan: &LogicalPlanRef) {
373        match plan.as_logical_share() {
374            Some(_) => self.visited(plan, |this| this.tree_visit(plan)),
375            None => self.tree_visit(plan),
376        }
377    }
378}
379
380impl<C: ConventionMarker> PlanRef<C> {
381    pub fn rewrite_exprs_recursive(&self, r: &mut impl ExprRewriter) -> PlanRef<C> {
382        self.rewrite_exprs_recursive_inner(r, &mut HashMap::new())
383    }
384
385    fn rewrite_exprs_recursive_inner(
386        &self,
387        r: &mut impl ExprRewriter,
388        rewritten_shares: &mut HashMap<ShareId, PlanRef<C>>,
389    ) -> PlanRef<C> {
390        if let Some(share) = self.as_share_node() {
391            let share_id = share.share_id();
392            if let Some(rewritten) = rewritten_shares.get(&share_id) {
393                return rewritten.clone();
394            }
395
396            let rewritten = self.rewrite_exprs(r);
397            let rewritten_share = rewritten
398                .as_share_node()
399                .expect("rewriting expressions must preserve a share node");
400            let input = rewritten_share
401                .input()
402                .rewrite_exprs_recursive_inner(r, rewritten_shares);
403            let rewritten = rewritten_share.replace_input(input);
404            rewritten_shares.insert(share_id, rewritten.clone());
405            return rewritten;
406        }
407
408        let new = self.rewrite_exprs(r);
409        let inputs: Vec<PlanRef<C>> = new
410            .inputs()
411            .iter()
412            .map(|plan_ref| plan_ref.rewrite_exprs_recursive_inner(r, rewritten_shares))
413            .collect();
414        new.clone_root_with_inputs(&inputs[..])
415    }
416}
417
418pub(crate) trait VisitExprsRecursive {
419    fn visit_exprs_recursive(&self, r: &mut impl ExprVisitor);
420}
421
422impl<C: ConventionMarker> VisitExprsRecursive for PlanRef<C> {
423    fn visit_exprs_recursive(&self, r: &mut impl ExprVisitor) {
424        self.visit_exprs_recursive_inner(r, &mut HashSet::new());
425    }
426}
427
428impl<C: ConventionMarker> PlanRef<C> {
429    fn visit_exprs_recursive_inner(
430        &self,
431        r: &mut impl ExprVisitor,
432        visited_shares: &mut HashSet<ShareId>,
433    ) {
434        if let Some(share) = self.as_share_node()
435            && !visited_shares.insert(share.share_id())
436        {
437            return;
438        }
439        self.visit_exprs(r);
440        self.inputs()
441            .iter()
442            .for_each(|plan_ref| plan_ref.visit_exprs_recursive_inner(r, visited_shares));
443    }
444}
445
446impl<C: ConventionMarker> PlanRef<C> {
447    pub fn expect_stream_key(&self) -> &[usize] {
448        self.stream_key().unwrap_or_else(|| {
449            panic!(
450                "a stream key is expected but not exist, plan:\n{}",
451                self.explain_to_string()
452            )
453        })
454    }
455}
456
457impl LogicalPlanRef {
458    fn prune_col_inner(
459        &self,
460        required_cols: &[usize],
461        ctx: &mut ColumnPruningContext,
462    ) -> LogicalPlanRef {
463        if let Some(logical_share) = self.as_logical_share() {
464            // A single-parent share is not a DAG boundary and can be removed immediately.
465            if ctx.get_parent_num(logical_share) == 1 {
466                return logical_share.input().prune_col(required_cols, ctx);
467            }
468
469            if ctx.is_collecting() {
470                if let Some(merged_required_cols) =
471                    ctx.add_required_cols(logical_share, required_cols.to_vec())
472                {
473                    // Continue collection below the share only after every parent has
474                    // contributed its requirement. Rewriting happens in the next phase.
475                    let _ = logical_share.input().prune_col(&merged_required_cols, ctx);
476                }
477
478                let mapping =
479                    ColIndexMapping::with_remaining_columns(required_cols, self.schema().len());
480                LogicalProject::with_mapping(self.clone(), mapping).into()
481            } else {
482                ctx.ensure_share_rebuilt(logical_share);
483                let share_mapping = ctx.share_mapping(logical_share);
484                let new_required_cols = required_cols
485                    .iter()
486                    .map(|&column| {
487                        share_mapping.try_map(column).unwrap_or_else(|| {
488                            panic!(
489                                "column {column} is absent from the collected mapping for share {:?}",
490                                logical_share.share_id()
491                            )
492                        })
493                    })
494                    .collect_vec();
495                let refreshed_share: LogicalPlanRef =
496                    LogicalShare::from_share_id(self.ctx(), logical_share.share_id()).into();
497                let mapping = ColIndexMapping::with_remaining_columns(
498                    &new_required_cols,
499                    refreshed_share.schema().len(),
500                );
501                LogicalProject::with_mapping(refreshed_share, mapping).into()
502            }
503        } else {
504            // Dispatch to dyn PlanNode instead of PlanRef.
505            let dyn_t = self.deref();
506            dyn_t.prune_col(required_cols, ctx)
507        }
508    }
509
510    fn predicate_pushdown_inner(
511        &self,
512        predicate: Condition,
513        ctx: &mut PredicatePushdownContext,
514    ) -> LogicalPlanRef {
515        if let Some(logical_share) = self.as_logical_share() {
516            // A single-parent share is not a DAG boundary and can be removed immediately.
517            if ctx.get_parent_num(logical_share) == 1 {
518                return logical_share.input().predicate_pushdown(predicate, ctx);
519            }
520
521            if ctx.is_collecting() {
522                if let Some(merged_predicate) = ctx.add_predicate(logical_share, predicate.clone())
523                {
524                    // Continue collection below the share only after every parent has
525                    // contributed. Rewriting happens in the next phase.
526                    let _ = logical_share
527                        .input()
528                        .predicate_pushdown(merged_predicate, ctx);
529                }
530                LogicalFilter::create(self.clone(), predicate)
531            } else {
532                ctx.ensure_share_rebuilt(logical_share);
533                let refreshed_share: LogicalPlanRef =
534                    LogicalShare::from_share_id(self.ctx(), logical_share.share_id()).into();
535                LogicalFilter::create(refreshed_share, predicate)
536            }
537        } else {
538            // Dispatch to dyn PlanNode instead of PlanRef.
539            let dyn_t = self.deref();
540            dyn_t.predicate_pushdown(predicate, ctx)
541        }
542    }
543
544    pub fn forbid_snapshot_backfill(&self) -> Option<String> {
545        struct ForbidSnapshotBackfill {
546            warning_msg: Option<String>,
547        }
548        impl LogicalPlanVisitor for ForbidSnapshotBackfill {
549            type Result = ();
550
551            type DefaultBehavior = impl DefaultBehavior<Self::Result>;
552
553            fn default_behavior() -> Self::DefaultBehavior {
554                DefaultValue
555            }
556
557            fn visit_logical_join(&mut self, plan: &LogicalJoin) -> Self::Result {
558                self.visit(plan.left());
559                self.visit(plan.right());
560                if self.warning_msg.is_none() && plan.should_be_temporal_join() {
561                    self.warning_msg =
562                        Some("snapshot backfill disabled due to temporal join".to_owned());
563                }
564            }
565
566            fn visit_logical_source(&mut self, plan: &LogicalSource) -> Self::Result {
567                if self.warning_msg.is_none() && plan.is_shared_source() {
568                    self.warning_msg = Some(format!(
569                        "snapshot backfill disabled due to using shared source {:?}",
570                        plan.core.catalog.as_ref().map(|c| &c.name)
571                    ));
572                }
573            }
574        }
575        let mut forbid_snapshot = ForbidSnapshotBackfill { warning_msg: None };
576        forbid_snapshot.visit(self.clone());
577        forbid_snapshot.warning_msg
578    }
579}
580
581impl ColPrunable for LogicalPlanRef {
582    fn prune_col(&self, required_cols: &[usize], ctx: &mut ColumnPruningContext) -> LogicalPlanRef {
583        let res = if ctx.is_running() {
584            self.prune_col_inner(required_cols, ctx)
585        } else {
586            ctx.run(self.clone(), required_cols)
587        };
588        #[cfg(debug_assertions)]
589        super::heuristic_optimizer::HeuristicOptimizer::check_equivalent_plan(
590            "column pruning",
591            &LogicalProject::with_out_col_idx(self.clone(), required_cols.iter().cloned()).into(),
592            &res,
593        );
594        res
595    }
596}
597
598impl PredicatePushdown for LogicalPlanRef {
599    fn predicate_pushdown(
600        &self,
601        predicate: Condition,
602        ctx: &mut PredicatePushdownContext,
603    ) -> LogicalPlanRef {
604        #[cfg(debug_assertions)]
605        let predicate_clone = predicate.clone();
606
607        let res = if ctx.is_running() {
608            self.predicate_pushdown_inner(predicate, ctx)
609        } else {
610            ctx.run(self.clone(), predicate)
611        };
612
613        #[cfg(debug_assertions)]
614        super::heuristic_optimizer::HeuristicOptimizer::check_equivalent_plan(
615            "predicate push down",
616            &LogicalFilter::new(self.clone(), predicate_clone).into(),
617            &res,
618        );
619
620        res
621    }
622}
623
624impl<C: ConventionMarker> PlanRef<C> {
625    pub fn clone_root_with_inputs(&self, inputs: &[PlanRef<C>]) -> PlanRef<C> {
626        if let Some(share) = self.as_share_node() {
627            assert_eq!(inputs.len(), 1);
628            share.replace_input(inputs[0].clone())
629        } else {
630            // Dispatch to dyn PlanNode instead of PlanRef.
631            let dyn_t = self.deref();
632            dyn_t.clone_with_inputs(inputs)
633        }
634    }
635}
636
637/// Implement again for the `dyn` newtype wrapper.
638impl<C: ConventionMarker> PlanRef<C> {
639    pub fn node_type(&self) -> C::PlanNodeType {
640        self.0.node_type()
641    }
642
643    pub fn plan_base(&self) -> &PlanBase<C> {
644        self.0.plan_base()
645    }
646}
647
648/// Allow access to all fields defined in [`GenericPlanRef`] for the type-erased plan node.
649// TODO: may also implement on `dyn PlanNode` directly.
650impl<C: ConventionMarker> GenericPlanRef for PlanRef<C> {
651    fn id(&self) -> PlanNodeId {
652        self.plan_base().id()
653    }
654
655    fn schema(&self) -> &Schema {
656        self.plan_base().schema()
657    }
658
659    fn stream_key(&self) -> Option<&[usize]> {
660        self.plan_base().stream_key()
661    }
662
663    fn ctx(&self) -> OptimizerContextRef {
664        self.plan_base().ctx()
665    }
666
667    fn functional_dependency(&self) -> &FunctionalDependencySet {
668        self.plan_base().functional_dependency()
669    }
670}
671
672/// Allow access to all fields defined in [`PhysicalPlanRef`] for the type-erased plan node.
673// TODO: may also implement on `dyn PlanNode` directly.
674impl PhysicalPlanRef for BatchPlanRef {
675    fn distribution(&self) -> &Distribution {
676        self.plan_base().distribution()
677    }
678}
679
680impl PhysicalPlanRef for StreamPlanRef {
681    fn distribution(&self) -> &Distribution {
682        self.plan_base().distribution()
683    }
684}
685
686/// Allow access to all fields defined in [`StreamPlanNodeMetadata`] for the type-erased plan node.
687// TODO: may also implement on `dyn PlanNode` directly.
688impl StreamPlanNodeMetadata for StreamPlanRef {
689    fn stream_kind(&self) -> StreamKind {
690        self.plan_base().stream_kind()
691    }
692
693    fn emit_on_window_close(&self) -> bool {
694        self.plan_base().emit_on_window_close()
695    }
696
697    fn watermark_columns(&self) -> &WatermarkColumns {
698        self.plan_base().watermark_columns()
699    }
700
701    fn columns_monotonicity(&self) -> &MonotonicityMap {
702        self.plan_base().columns_monotonicity()
703    }
704}
705
706/// Allow access to all fields defined in [`BatchPlanNodeMetadata`] for the type-erased plan node.
707// TODO: may also implement on `dyn PlanNode` directly.
708impl BatchPlanNodeMetadata for BatchPlanRef {
709    fn order(&self) -> &Order {
710        self.plan_base().order()
711    }
712
713    fn orders(&self) -> Vec<Order> {
714        self.plan_base().orders()
715    }
716}
717
718/// In order to let expression display id started from 1 for explaining, hidden column names and
719/// other places. We will reset expression display id to 0 and clone the whole plan to reset the
720/// schema.
721pub fn reorganize_elements_id<C: ConventionMarker>(plan: PlanRef<C>) -> PlanRef<C> {
722    let backup = plan.ctx().backup_elem_ids();
723    plan.ctx().reset_elem_ids();
724    let plan = PlanCloner::clone_whole_plan(plan);
725    plan.ctx().restore_elem_ids(backup);
726    plan
727}
728
729pub trait Explain {
730    /// Write explain the whole plan tree.
731    fn explain<'a>(&self) -> Pretty<'a>;
732
733    /// Write explain the whole plan tree with node id.
734    fn explain_with_id<'a>(&self) -> Pretty<'a>;
735
736    /// Explain the plan node and return a string.
737    fn explain_to_string(&self) -> String;
738
739    /// Explain the plan node and return a json string.
740    fn explain_to_json(&self) -> String;
741
742    /// Explain the plan node and return a xml string.
743    fn explain_to_xml(&self) -> String;
744
745    /// Explain the plan node and return a yaml string.
746    fn explain_to_yaml(&self) -> String;
747
748    /// Explain the plan node and return a dot format string.
749    fn explain_to_dot(&self) -> String;
750}
751
752impl<C: ConventionMarker> Explain for PlanRef<C> {
753    /// Write explain the whole plan tree.
754    fn explain<'a>(&self) -> Pretty<'a> {
755        let mut node = self.distill();
756        let inputs = self.inputs();
757        for input in inputs.iter().peekable() {
758            node.children.push(input.explain());
759        }
760        Pretty::Record(node)
761    }
762
763    /// Write explain the whole plan tree with node id.
764    fn explain_with_id<'a>(&self) -> Pretty<'a> {
765        let node_id = self.id();
766        let mut node = self.distill();
767        // NOTE(kwannoel): Can lead to poor performance if plan is very large,
768        // but we want to show the id first.
769        node.fields
770            .insert(0, ("id".into(), Pretty::display(&node_id.0)));
771        let inputs = self.inputs();
772        for input in inputs.iter().peekable() {
773            node.children.push(input.explain_with_id());
774        }
775        Pretty::Record(node)
776    }
777
778    /// Explain the plan node and return a string.
779    fn explain_to_string(&self) -> String {
780        let plan = reorganize_elements_id(self.clone());
781
782        let mut output = String::with_capacity(2048);
783        let mut config = pretty_config();
784        config.unicode(&mut output, &plan.explain());
785        output
786    }
787
788    /// Explain the plan node and return a json string.
789    fn explain_to_json(&self) -> String {
790        let plan = reorganize_elements_id(self.clone());
791        let explain_ir = plan.explain();
792        serde_json::to_string_pretty(&PrettySerde(explain_ir, true))
793            .expect("failed to serialize plan to json")
794    }
795
796    /// Explain the plan node and return a xml string.
797    fn explain_to_xml(&self) -> String {
798        let plan = reorganize_elements_id(self.clone());
799        let explain_ir = plan.explain();
800        quick_xml::se::to_string(&PrettySerde(explain_ir, true))
801            .expect("failed to serialize plan to xml")
802    }
803
804    /// Explain the plan node and return a yaml string.
805    fn explain_to_yaml(&self) -> String {
806        let plan = reorganize_elements_id(self.clone());
807        let explain_ir = plan.explain();
808        serde_yaml::to_string(&PrettySerde(explain_ir, true))
809            .expect("failed to serialize plan to yaml")
810    }
811
812    /// Explain the plan node and return a dot format string.
813    fn explain_to_dot(&self) -> String {
814        let plan = reorganize_elements_id(self.clone());
815        let explain_ir = plan.explain_with_id();
816        let mut graph = Graph::<String, String>::new();
817        let mut nodes = HashMap::new();
818        build_graph_from_pretty(&explain_ir, &mut graph, &mut nodes, None);
819        let dot = Dot::with_config(&graph, &[Config::EdgeNoLabel]);
820        dot.to_string()
821    }
822}
823
824impl<C: ConventionMarker> PlanRef<C> {
825    pub fn as_share_node(&self) -> Option<&C::ShareNode> {
826        C::as_share(self)
827    }
828}
829
830pub(crate) fn pretty_config() -> PrettyConfig {
831    PrettyConfig {
832        indent: 3,
833        need_boundaries: false,
834        width: 2048,
835        reduced_spaces: true,
836    }
837}
838
839macro_rules! impl_generic_plan_ref_method {
840    ($($convention:ident),+) => {
841        paste! {
842            $(
843                /// Directly implement methods for `PlanNode` to access the fields defined in [`GenericPlanRef`].
844                impl dyn [<$convention PlanNode>] {
845                    pub fn id(&self) -> PlanNodeId {
846                        self.plan_base().id()
847                    }
848
849                    pub fn ctx(&self) -> OptimizerContextRef {
850                        self.plan_base().ctx().clone()
851                    }
852
853                    pub fn schema(&self) -> &Schema {
854                        self.plan_base().schema()
855                    }
856
857                    pub fn stream_key(&self) -> Option<&[usize]> {
858                        self.plan_base().stream_key()
859                    }
860
861                    pub fn functional_dependency(&self) -> &FunctionalDependencySet {
862                        self.plan_base().functional_dependency()
863                    }
864
865                    pub fn explain_myself_to_string(&self) -> String {
866                        self.distill_to_string()
867                    }
868                }
869            )+
870        }
871    };
872}
873
874impl_generic_plan_ref_method!(Batch, Stream, Logical);
875
876/// Recursion depth threshold for plan node visitor to send notice to user.
877pub const PLAN_DEPTH_THRESHOLD: usize = 30;
878/// Notice message for plan node visitor to send to user when the depth threshold is reached.
879pub const PLAN_TOO_DEEP_NOTICE: &str = "The plan is too deep. \
880Consider simplifying or splitting the query if you encounter any issues.";
881
882impl dyn StreamPlanNode {
883    /// Serialize the plan node and its children to a stream plan proto.
884    ///
885    /// Note that some operators has their own implementation of `to_stream_prost`. We have a
886    /// hook inside to do some ad-hoc things.
887    pub fn to_stream_prost(
888        &self,
889        state: &mut BuildFragmentGraphState,
890    ) -> SchedulerResult<PbStreamPlan> {
891        recursive::tracker!().recurse(|t| {
892            if t.depth_reaches(PLAN_DEPTH_THRESHOLD) {
893                notice_to_user(PLAN_TOO_DEEP_NOTICE);
894            }
895
896            use stream::prelude::*;
897
898            if let Some(stream_table_scan) = self.as_stream_table_scan() {
899                return stream_table_scan.adhoc_to_stream_prost(state);
900            }
901            if let Some(stream_cdc_table_scan) = self.as_stream_cdc_table_scan() {
902                return stream_cdc_table_scan.adhoc_to_stream_prost(state);
903            }
904            if let Some(stream_source_scan) = self.as_stream_source_scan() {
905                return stream_source_scan.adhoc_to_stream_prost(state);
906            }
907            if let Some(stream_share) = self.as_stream_share() {
908                return stream_share.adhoc_to_stream_prost(state);
909            }
910            if let Some(writer) = self.as_stream_iceberg_with_pk_index_writer() {
911                return writer.adhoc_to_stream_prost(state);
912            }
913
914            let node = Some(self.try_to_stream_prost_body(state)?);
915            let input = self
916                .inputs()
917                .into_iter()
918                .map(|plan| plan.to_stream_prost(state))
919                .try_collect()?;
920            // TODO: support pk_indices and operator_id
921            Ok(PbStreamPlan {
922                input,
923                identity: self.explain_myself_to_string(),
924                node_body: node,
925                operator_id: self.id().to_stream_node_operator_id(),
926                stream_key: self
927                    .stream_key()
928                    .unwrap_or_default()
929                    .iter()
930                    .map(|x| *x as u32)
931                    .collect(),
932                fields: self.schema().to_prost(),
933                stream_kind: self.plan_base().stream_kind().to_protobuf() as i32,
934            })
935        })
936    }
937}
938
939impl dyn BatchPlanNode {
940    /// Serialize the plan node and its children to a batch plan proto.
941    pub fn to_batch_prost(&self) -> SchedulerResult<PbBatchPlan> {
942        self.to_batch_prost_identity(true)
943    }
944
945    /// Serialize the plan node and its children to a batch plan proto without the identity field
946    /// (for testing).
947    pub fn to_batch_prost_identity(&self, identity: bool) -> SchedulerResult<PbBatchPlan> {
948        recursive::tracker!().recurse(|t| {
949            if t.depth_reaches(PLAN_DEPTH_THRESHOLD) {
950                notice_to_user(PLAN_TOO_DEEP_NOTICE);
951            }
952
953            let node_body = Some(self.try_to_batch_prost_body()?);
954            let children = self
955                .inputs()
956                .into_iter()
957                .map(|plan| plan.to_batch_prost_identity(identity))
958                .try_collect()?;
959            Ok(PbBatchPlan {
960                children,
961                identity: if identity {
962                    self.explain_myself_to_string()
963                } else {
964                    "".into()
965                },
966                node_body,
967            })
968        })
969    }
970}
971
972mod plan_base;
973pub use plan_base::*;
974#[macro_use]
975mod plan_tree_node;
976pub use plan_tree_node::*;
977mod col_pruning;
978pub use col_pruning::*;
979mod expr_rewritable;
980pub use expr_rewritable::*;
981mod expr_visitable;
982
983mod convert;
984pub use convert::*;
985mod eq_join_predicate;
986pub use eq_join_predicate::*;
987mod to_prost;
988pub use to_prost::*;
989mod predicate_pushdown;
990pub use predicate_pushdown::*;
991mod merge_eq_nodes;
992pub use merge_eq_nodes::*;
993
994pub mod batch;
995pub mod generic;
996pub mod stream;
997
998pub use generic::{PlanAggCall, PlanAggCallDisplay};
999
1000mod batch_delete;
1001mod batch_exchange;
1002mod batch_expand;
1003mod batch_filter;
1004mod batch_get_channel_delta_stats;
1005mod batch_group_topn;
1006mod batch_hash_agg;
1007mod batch_hash_join;
1008mod batch_hop_window;
1009mod batch_insert;
1010mod batch_limit;
1011mod batch_log_seq_scan;
1012mod batch_lookup_join;
1013mod batch_max_one_row;
1014mod batch_nested_loop_join;
1015mod batch_over_window;
1016mod batch_project;
1017mod batch_project_set;
1018mod batch_seq_scan;
1019mod batch_simple_agg;
1020mod batch_sort;
1021mod batch_sort_agg;
1022mod batch_source;
1023mod batch_sys_seq_scan;
1024mod batch_table_function;
1025mod batch_topn;
1026mod batch_union;
1027mod batch_update;
1028mod batch_values;
1029mod logical_agg;
1030mod logical_apply;
1031mod logical_cdc_scan;
1032mod logical_changelog;
1033mod logical_dedup;
1034mod logical_delete;
1035mod logical_except;
1036mod logical_expand;
1037mod logical_filter;
1038mod logical_gap_fill;
1039mod logical_get_channel_delta_stats;
1040mod logical_hop_window;
1041mod logical_insert;
1042mod logical_intersect;
1043mod logical_join;
1044mod logical_kafka_scan;
1045mod logical_limit;
1046mod logical_locality_provider;
1047mod logical_match_recognize;
1048mod logical_max_one_row;
1049mod logical_multi_join;
1050mod logical_now;
1051mod logical_over_window;
1052mod logical_project;
1053mod logical_project_set;
1054mod logical_scan;
1055mod logical_share;
1056mod logical_source;
1057mod logical_sys_scan;
1058mod logical_table_function;
1059mod logical_topn;
1060mod logical_union;
1061mod logical_update;
1062mod logical_values;
1063mod stream_asof_join;
1064mod stream_changelog;
1065mod stream_dedup;
1066mod stream_delta_join;
1067mod stream_dml;
1068mod stream_dynamic_filter;
1069mod stream_eowc_gap_fill;
1070mod stream_eowc_over_window;
1071mod stream_exchange;
1072mod stream_expand;
1073mod stream_filter;
1074mod stream_fs_fetch;
1075mod stream_gap_fill;
1076mod stream_global_approx_percentile;
1077mod stream_group_topn;
1078mod stream_hash_agg;
1079mod stream_hash_join;
1080mod stream_hop_window;
1081mod stream_iceberg_with_pk_index_position_delete_merger;
1082mod stream_iceberg_with_pk_index_writer;
1083mod stream_join_common;
1084mod stream_local_approx_percentile;
1085mod stream_locality_provider;
1086mod stream_match_recognize;
1087mod stream_materialize;
1088mod stream_materialized_exprs;
1089mod stream_now;
1090mod stream_over_window;
1091mod stream_project;
1092mod stream_project_set;
1093mod stream_row_id_gen;
1094mod stream_row_merge;
1095mod stream_simple_agg;
1096mod stream_sink;
1097mod stream_sort;
1098mod stream_source;
1099mod stream_source_scan;
1100mod stream_stateless_simple_agg;
1101mod stream_sync_log_store;
1102mod stream_table_scan;
1103mod stream_topn;
1104mod stream_union;
1105mod stream_values;
1106mod stream_watermark_filter;
1107
1108mod batch_file_scan;
1109mod batch_iceberg_metadata_scan;
1110mod batch_iceberg_scan;
1111mod batch_kafka_scan;
1112mod batch_postgres_query;
1113
1114mod batch_mysql_query;
1115mod derive;
1116mod logical_file_scan;
1117mod logical_iceberg_intermediate_scan;
1118mod logical_iceberg_metadata_scan;
1119mod logical_iceberg_scan;
1120mod logical_postgres_query;
1121
1122mod batch_vector_search;
1123mod logical_mysql_query;
1124mod logical_vector_search;
1125mod logical_vector_search_lookup_join;
1126mod stream_cdc_table_scan;
1127mod stream_share;
1128mod stream_temporal_join;
1129mod stream_upstream_sink_union;
1130mod stream_vector_index_lookup_join;
1131mod stream_vector_index_write;
1132pub mod utils;
1133
1134pub use batch_delete::BatchDelete;
1135pub use batch_exchange::BatchExchange;
1136pub use batch_expand::BatchExpand;
1137pub use batch_file_scan::BatchFileScan;
1138pub use batch_filter::BatchFilter;
1139pub use batch_get_channel_delta_stats::BatchGetChannelDeltaStats;
1140pub use batch_group_topn::BatchGroupTopN;
1141pub use batch_hash_agg::BatchHashAgg;
1142pub use batch_hash_join::BatchHashJoin;
1143pub use batch_hop_window::BatchHopWindow;
1144pub use batch_iceberg_metadata_scan::BatchIcebergMetadataScan;
1145pub use batch_iceberg_scan::BatchIcebergScan;
1146pub use batch_insert::BatchInsert;
1147pub use batch_kafka_scan::BatchKafkaScan;
1148pub use batch_limit::BatchLimit;
1149pub use batch_log_seq_scan::BatchLogSeqScan;
1150pub use batch_lookup_join::BatchLookupJoin;
1151pub use batch_max_one_row::BatchMaxOneRow;
1152pub use batch_mysql_query::BatchMySqlQuery;
1153pub use batch_nested_loop_join::BatchNestedLoopJoin;
1154pub use batch_over_window::BatchOverWindow;
1155pub use batch_postgres_query::BatchPostgresQuery;
1156pub use batch_project::BatchProject;
1157pub use batch_project_set::BatchProjectSet;
1158pub use batch_seq_scan::BatchSeqScan;
1159pub use batch_simple_agg::BatchSimpleAgg;
1160pub use batch_sort::BatchSort;
1161pub use batch_sort_agg::BatchSortAgg;
1162pub use batch_source::BatchSource;
1163pub use batch_sys_seq_scan::BatchSysSeqScan;
1164pub use batch_table_function::BatchTableFunction;
1165pub use batch_topn::BatchTopN;
1166pub use batch_union::BatchUnion;
1167pub use batch_update::BatchUpdate;
1168pub use batch_values::BatchValues;
1169pub use batch_vector_search::BatchVectorSearch;
1170pub use logical_agg::LogicalAgg;
1171pub use logical_apply::LogicalApply;
1172pub use logical_cdc_scan::LogicalCdcScan;
1173pub use logical_changelog::LogicalChangeLog;
1174pub use logical_dedup::LogicalDedup;
1175pub use logical_delete::LogicalDelete;
1176pub use logical_except::LogicalExcept;
1177pub use logical_expand::LogicalExpand;
1178pub use logical_file_scan::LogicalFileScan;
1179pub use logical_filter::LogicalFilter;
1180pub use logical_gap_fill::LogicalGapFill;
1181pub use logical_get_channel_delta_stats::LogicalGetChannelDeltaStats;
1182pub use logical_hop_window::LogicalHopWindow;
1183pub use logical_iceberg_intermediate_scan::{HummockRewriteInfo, LogicalIcebergIntermediateScan};
1184pub use logical_iceberg_metadata_scan::LogicalIcebergMetadataScan;
1185pub use logical_iceberg_scan::LogicalIcebergScan;
1186pub use logical_insert::LogicalInsert;
1187pub use logical_intersect::LogicalIntersect;
1188pub use logical_join::LogicalJoin;
1189pub use logical_kafka_scan::LogicalKafkaScan;
1190pub use logical_limit::LogicalLimit;
1191pub use logical_locality_provider::LogicalLocalityProvider;
1192pub use logical_match_recognize::LogicalMatchRecognize;
1193pub use logical_max_one_row::LogicalMaxOneRow;
1194pub use logical_multi_join::{LogicalMultiJoin, LogicalMultiJoinBuilder};
1195pub use logical_mysql_query::LogicalMySqlQuery;
1196pub use logical_now::LogicalNow;
1197pub use logical_over_window::LogicalOverWindow;
1198pub use logical_postgres_query::LogicalPostgresQuery;
1199pub use logical_project::LogicalProject;
1200pub use logical_project_set::LogicalProjectSet;
1201pub use logical_scan::LogicalScan;
1202pub use logical_share::LogicalShare;
1203pub use logical_source::LogicalSource;
1204pub use logical_sys_scan::LogicalSysScan;
1205pub use logical_table_function::LogicalTableFunction;
1206pub use logical_topn::LogicalTopN;
1207pub use logical_union::LogicalUnion;
1208pub use logical_update::LogicalUpdate;
1209pub use logical_values::LogicalValues;
1210pub use logical_vector_search::LogicalVectorSearch;
1211pub use logical_vector_search_lookup_join::LogicalVectorSearchLookupJoin;
1212use risingwave_pb::id::StreamNodeLocalOperatorId;
1213pub use stream_asof_join::StreamAsOfJoin;
1214pub use stream_cdc_table_scan::StreamCdcTableScan;
1215pub use stream_changelog::StreamChangeLog;
1216pub use stream_dedup::StreamDedup;
1217pub use stream_delta_join::StreamDeltaJoin;
1218pub use stream_dml::StreamDml;
1219pub use stream_dynamic_filter::StreamDynamicFilter;
1220pub use stream_eowc_gap_fill::StreamEowcGapFill;
1221pub use stream_eowc_over_window::StreamEowcOverWindow;
1222pub use stream_exchange::StreamExchange;
1223pub use stream_expand::StreamExpand;
1224pub use stream_filter::StreamFilter;
1225pub use stream_fs_fetch::StreamFsFetch;
1226pub use stream_gap_fill::StreamGapFill;
1227pub use stream_global_approx_percentile::StreamGlobalApproxPercentile;
1228pub use stream_group_topn::StreamGroupTopN;
1229pub use stream_hash_agg::StreamHashAgg;
1230pub use stream_hash_join::StreamHashJoin;
1231pub use stream_hop_window::StreamHopWindow;
1232pub use stream_iceberg_with_pk_index_position_delete_merger::StreamIcebergWithPkIndexPositionDeleteMerger;
1233pub use stream_iceberg_with_pk_index_writer::StreamIcebergWithPkIndexWriter;
1234use stream_join_common::StreamJoinCommon;
1235pub use stream_local_approx_percentile::StreamLocalApproxPercentile;
1236pub use stream_locality_provider::StreamLocalityProvider;
1237pub use stream_match_recognize::StreamMatchRecognize;
1238pub use stream_materialize::StreamMaterialize;
1239pub use stream_materialized_exprs::StreamMaterializedExprs;
1240pub use stream_now::StreamNow;
1241pub use stream_over_window::StreamOverWindow;
1242pub use stream_project::StreamProject;
1243pub use stream_project_set::StreamProjectSet;
1244pub use stream_row_id_gen::StreamRowIdGen;
1245pub use stream_row_merge::StreamRowMerge;
1246pub use stream_share::StreamShare;
1247pub use stream_simple_agg::StreamSimpleAgg;
1248pub use stream_sink::{IcebergPartitionInfo, PartitionComputeInfo, StreamSink};
1249pub use stream_sort::StreamEowcSort;
1250pub use stream_source::StreamSource;
1251pub use stream_source_scan::StreamSourceScan;
1252pub use stream_stateless_simple_agg::StreamStatelessSimpleAgg;
1253pub use stream_sync_log_store::StreamSyncLogStore;
1254pub(crate) use stream_sync_log_store::ensure_sync_log_store_fragment_root;
1255pub use stream_table_scan::StreamTableScan;
1256pub use stream_temporal_join::StreamTemporalJoin;
1257pub use stream_topn::StreamTopN;
1258pub use stream_union::StreamUnion;
1259pub use stream_upstream_sink_union::StreamUpstreamSinkUnion;
1260pub use stream_values::StreamValues;
1261pub use stream_vector_index_lookup_join::StreamVectorIndexLookupJoin;
1262pub use stream_vector_index_write::StreamVectorIndexWrite;
1263pub use stream_watermark_filter::StreamWatermarkFilter;
1264
1265use crate::expr::{ExprRewriter, ExprVisitor, Literal};
1266use crate::optimizer::optimizer_context::OptimizerContextRef;
1267use crate::optimizer::plan_node::expr_visitable::ExprVisitable;
1268use crate::optimizer::plan_rewriter::PlanCloner;
1269use crate::optimizer::plan_visitor::{DefaultBehavior, DefaultValue, LogicalPlanVisitor};
1270use crate::scheduler::SchedulerResult;
1271use crate::stream_fragmenter::BuildFragmentGraphState;
1272use crate::utils::{ColIndexMapping, Condition, DynEq, DynHash, Endo, Layer, Visit};
1273
1274/// `for_all_plan_nodes` includes all plan nodes. If you added a new plan node
1275/// inside the project, be sure to add here and in its conventions like `for_logical_plan_nodes`
1276///
1277/// Every tuple has two elements, where `{ convention, name }`
1278/// You can use it as follows
1279/// ```rust
1280/// macro_rules! use_plan {
1281///     ($({ $convention:ident, $name:ident }),*) => {};
1282/// }
1283/// risingwave_frontend::for_all_plan_nodes! { use_plan }
1284/// ```
1285/// See the following implementations for example.
1286#[macro_export]
1287macro_rules! for_all_plan_nodes {
1288    ($macro:path $(,$rest:tt)*) => {
1289        $macro! {
1290              { Logical, Agg }
1291            , { Logical, Apply }
1292            , { Logical, Filter }
1293            , { Logical, Project }
1294            , { Logical, Scan }
1295            , { Logical, CdcScan }
1296            , { Logical, SysScan }
1297            , { Logical, Source }
1298            , { Logical, Insert }
1299            , { Logical, Delete }
1300            , { Logical, Update }
1301            , { Logical, Join }
1302            , { Logical, Values }
1303            , { Logical, Limit }
1304            , { Logical, TopN }
1305            , { Logical, HopWindow }
1306            , { Logical, TableFunction }
1307            , { Logical, MultiJoin }
1308            , { Logical, Expand }
1309            , { Logical, ProjectSet }
1310            , { Logical, Union }
1311            , { Logical, OverWindow }
1312            , { Logical, Share }
1313            , { Logical, Now }
1314            , { Logical, Dedup }
1315            , { Logical, Intersect }
1316            , { Logical, Except }
1317            , { Logical, MatchRecognize }
1318            , { Logical, MaxOneRow }
1319            , { Logical, KafkaScan }
1320            , { Logical, IcebergScan }
1321            , { Logical, IcebergMetadataScan }
1322            , { Logical, IcebergIntermediateScan }
1323            , { Logical, ChangeLog }
1324            , { Logical, FileScan }
1325            , { Logical, PostgresQuery }
1326            , { Logical, MySqlQuery }
1327            , { Logical, GapFill }
1328            , { Logical, VectorSearch }
1329            , { Logical, GetChannelDeltaStats }
1330            , { Logical, LocalityProvider }
1331            , { Logical, VectorSearchLookupJoin }
1332            , { Batch, SimpleAgg }
1333            , { Batch, HashAgg }
1334            , { Batch, SortAgg }
1335            , { Batch, Project }
1336            , { Batch, Filter }
1337            , { Batch, Insert }
1338            , { Batch, Delete }
1339            , { Batch, Update }
1340            , { Batch, SeqScan }
1341            , { Batch, SysSeqScan }
1342            , { Batch, LogSeqScan }
1343            , { Batch, HashJoin }
1344            , { Batch, NestedLoopJoin }
1345            , { Batch, Values }
1346            , { Batch, Sort }
1347            , { Batch, Exchange }
1348            , { Batch, Limit }
1349            , { Batch, TopN }
1350            , { Batch, HopWindow }
1351            , { Batch, TableFunction }
1352            , { Batch, Expand }
1353            , { Batch, LookupJoin }
1354            , { Batch, ProjectSet }
1355            , { Batch, Union }
1356            , { Batch, GroupTopN }
1357            , { Batch, Source }
1358            , { Batch, OverWindow }
1359            , { Batch, MaxOneRow }
1360            , { Batch, KafkaScan }
1361            , { Batch, IcebergScan }
1362            , { Batch, IcebergMetadataScan }
1363            , { Batch, FileScan }
1364            , { Batch, PostgresQuery }
1365            , { Batch, MySqlQuery }
1366            , { Batch, GetChannelDeltaStats }
1367            , { Batch, VectorSearch }
1368            , { Stream, Project }
1369            , { Stream, Filter }
1370            , { Stream, TableScan }
1371            , { Stream, CdcTableScan }
1372            , { Stream, Sink }
1373            , { Stream, Source }
1374            , { Stream, SourceScan }
1375            , { Stream, HashJoin }
1376            , { Stream, Exchange }
1377            , { Stream, HashAgg }
1378            , { Stream, SimpleAgg }
1379            , { Stream, StatelessSimpleAgg }
1380            , { Stream, Materialize }
1381            , { Stream, TopN }
1382            , { Stream, HopWindow }
1383            , { Stream, DeltaJoin }
1384            , { Stream, Expand }
1385            , { Stream, DynamicFilter }
1386            , { Stream, ProjectSet }
1387            , { Stream, GroupTopN }
1388            , { Stream, Union }
1389            , { Stream, RowIdGen }
1390            , { Stream, Dml }
1391            , { Stream, Now }
1392            , { Stream, Share }
1393            , { Stream, WatermarkFilter }
1394            , { Stream, TemporalJoin }
1395            , { Stream, Values }
1396            , { Stream, Dedup }
1397            , { Stream, EowcOverWindow }
1398            , { Stream, EowcSort }
1399            , { Stream, MatchRecognize }
1400            , { Stream, OverWindow }
1401            , { Stream, FsFetch }
1402            , { Stream, ChangeLog }
1403            , { Stream, GlobalApproxPercentile }
1404            , { Stream, LocalApproxPercentile }
1405            , { Stream, RowMerge }
1406            , { Stream, AsOfJoin }
1407            , { Stream, SyncLogStore }
1408            , { Stream, MaterializedExprs }
1409            , { Stream, VectorIndexWrite }
1410            , { Stream, VectorIndexLookupJoin }
1411            , { Stream, UpstreamSinkUnion }
1412            , { Stream, LocalityProvider }
1413            , { Stream, EowcGapFill }
1414            , { Stream, GapFill }
1415            , { Stream, IcebergWithPkIndexWriter }
1416            , { Stream, IcebergWithPkIndexPositionDeleteMerger }
1417            $(,$rest)*
1418        }
1419    };
1420}
1421
1422#[macro_export]
1423macro_rules! for_each_convention_all_plan_nodes {
1424    ($macro:path $(,$rest:tt)*) => {
1425        $crate::for_all_plan_nodes! {
1426            $crate::for_each_convention_all_plan_nodes
1427            , $macro
1428            $(,$rest)*
1429        }
1430    };
1431    (
1432        $( { Logical, $logical_name:ident } ),*
1433        , $( { Batch, $batch_name:ident } ),*
1434        , $( { Stream, $stream_name:ident } ),*
1435        , $macro:path $(,$rest:tt)*
1436    ) => {
1437        $macro! {
1438            {
1439                Logical, { $( $logical_name ),* },
1440                Batch, { $( $batch_name ),* },
1441                Stream, { $( $stream_name ),* }
1442            }
1443            $(,$rest)*
1444        }
1445    }
1446}
1447
1448/// impl `PlanNodeType` fn for each node.
1449macro_rules! impl_plan_node_meta {
1450    ({
1451        $( $convention:ident, { $( $name:ident ),* }),*
1452    }) => {
1453        paste!{
1454            $(
1455                /// each enum value represent a `PlanNode` struct type, help us to dispatch and downcast
1456                #[derive(Copy, Clone, PartialEq, Debug, Hash, Eq, Serialize)]
1457                pub enum [<$convention PlanNodeType>] {
1458                    $( [<$convention $name>] ),*
1459                }
1460            )*
1461            $(
1462                $(impl PlanNodeMeta for [<$convention $name>] {
1463                    type Convention = $convention;
1464                    const NODE_TYPE: [<$convention PlanNodeType>] = [<$convention PlanNodeType>]::[<$convention $name>];
1465
1466                    fn plan_base(&self) -> &PlanBase<$convention> {
1467                        &self.base
1468                    }
1469                }
1470
1471                impl Deref for [<$convention $name>] {
1472                    type Target = PlanBase<$convention>;
1473
1474                    fn deref(&self) -> &Self::Target {
1475                        &self.base
1476                    }
1477                })*
1478            )*
1479        }
1480    }
1481}
1482
1483for_each_convention_all_plan_nodes! { impl_plan_node_meta }
1484
1485macro_rules! impl_plan_node {
1486    ($({ $convention:ident, $name:ident }),*) => {
1487        paste!{
1488            $(impl [<$convention PlanNode>] for [<$convention $name>] { })*
1489        }
1490    }
1491}
1492
1493for_all_plan_nodes! { impl_plan_node }
1494
1495/// impl plan node downcast fn for each node.
1496macro_rules! impl_down_cast_fn {
1497    ({
1498        $( $convention:ident, { $( $name:ident ),* }),*
1499    }) => {
1500        paste!{
1501            $(
1502                impl dyn [<$convention PlanNode>] {
1503                    $( pub fn [< as_ $convention:snake _ $name:snake>](&self) -> Option<&[<$convention $name>]> {
1504                        self.downcast_ref::<[<$convention $name>]>()
1505                    } )*
1506                }
1507            )*
1508        }
1509    }
1510}
1511
1512for_each_convention_all_plan_nodes! { impl_down_cast_fn }