risingwave_frontend/optimizer/plan_node/
logical_iceberg_scan.rs1use std::hash::{Hash, Hasher};
16
17use pretty_xmlish::{Pretty, XmlNode};
18use risingwave_connector::source::iceberg::IcebergFileScanTask;
19use risingwave_pb::batch_plan::iceberg_scan_node::IcebergScanType;
20
21use super::generic::GenericPlanRef;
22use super::utils::{Distill, childless_record};
23use super::{
24 ColPrunable, ExprRewritable, Logical, LogicalPlanRef as PlanRef, PlanBase, PredicatePushdown,
25 ToBatch, ToStream, generic,
26};
27use crate::catalog::source_catalog::SourceCatalog;
28use crate::error::Result;
29use crate::optimizer::plan_node::expr_visitable::ExprVisitable;
30use crate::optimizer::plan_node::utils::column_names_pretty;
31use crate::optimizer::plan_node::{
32 BatchIcebergScan, ColumnPruningContext, LogicalFilter, PredicatePushdownContext,
33 RewriteStreamContext, ToStreamContext,
34};
35use crate::utils::{ColIndexMapping, Condition};
36
37#[derive(Debug, Clone, PartialEq)]
46pub struct LogicalIcebergScan {
47 pub base: PlanBase<Logical>,
48 pub core: generic::Source,
49 pub task: IcebergFileScanTask,
50}
51
52impl Eq for LogicalIcebergScan {}
53
54impl Hash for LogicalIcebergScan {
55 fn hash<H: Hasher>(&self, state: &mut H) {
56 self.base.hash(state);
57 self.core.hash(state);
58 self.iceberg_scan_type().hash(state);
59 }
60}
61
62impl LogicalIcebergScan {
63 pub fn new(core: generic::Source, task: IcebergFileScanTask) -> Self {
64 let base = PlanBase::new_logical_with_core(&core);
65 LogicalIcebergScan { base, core, task }
66 }
67
68 pub fn source_catalog(&self) -> Option<&SourceCatalog> {
69 self.core.catalog.as_deref()
70 }
71
72 pub fn iceberg_scan_type(&self) -> IcebergScanType {
73 self.task.scan_type()
74 }
75}
76
77impl_plan_tree_node_for_leaf! { Logical, LogicalIcebergScan}
78impl Distill for LogicalIcebergScan {
79 fn distill<'a>(&self) -> XmlNode<'a> {
80 let fields = if let Some(catalog) = self.source_catalog() {
81 let src = Pretty::from(catalog.name.clone());
82 vec![
83 ("source", src),
84 ("columns", column_names_pretty(self.schema())),
85 (
86 "iceberg_scan_type",
87 Pretty::debug(&self.iceberg_scan_type()),
88 ),
89 ]
90 } else {
91 vec![]
92 };
93 childless_record("LogicalIcebergScan", fields)
94 }
95}
96
97impl ColPrunable for LogicalIcebergScan {
98 fn prune_col(&self, _: &[usize], _: &mut ColumnPruningContext) -> PlanRef {
99 unreachable!()
101 }
102}
103
104impl ExprRewritable<Logical> for LogicalIcebergScan {}
105
106impl ExprVisitable for LogicalIcebergScan {}
107
108impl PredicatePushdown for LogicalIcebergScan {
109 fn predicate_pushdown(
110 &self,
111 predicate: Condition,
112 _ctx: &mut PredicatePushdownContext,
113 ) -> PlanRef {
114 LogicalFilter::create(self.clone().into(), predicate)
116 }
117}
118
119impl ToBatch for LogicalIcebergScan {
120 fn to_batch(&self) -> Result<crate::optimizer::plan_node::BatchPlanRef> {
121 let plan = BatchIcebergScan::new(self.core.clone(), self.task.clone()).into();
122 Ok(plan)
123 }
124}
125
126impl ToStream for LogicalIcebergScan {
127 fn to_stream(
128 &self,
129 _ctx: &mut ToStreamContext,
130 ) -> Result<crate::optimizer::plan_node::StreamPlanRef> {
131 unreachable!()
132 }
133
134 fn logical_rewrite_for_stream(
135 &self,
136 _ctx: &mut RewriteStreamContext,
137 ) -> Result<(PlanRef, ColIndexMapping)> {
138 unreachable!()
139 }
140}