risingwave_frontend/optimizer/plan_node/
stream_over_window.rs1use std::collections::HashSet;
16
17use risingwave_common::util::sort_util::{ColumnOrder, OrderType};
18use risingwave_expr::window_function::FrameBounds;
19use risingwave_pb::stream_plan::stream_node::PbNodeBody;
20
21use super::generic::{GenericPlanNode, PlanWindowFunction};
22use super::stream::prelude::*;
23use super::utils::{TableCatalogBuilder, impl_distill_by_unit};
24use super::{
25 ExprRewritable, PlanBase, PlanTreeNodeUnary, StreamNode, StreamPlanRef as PlanRef, generic,
26};
27use crate::TableCatalog;
28use crate::optimizer::plan_node::expr_visitable::ExprVisitable;
29use crate::optimizer::property::{MonotonicityMap, WatermarkColumns};
30use crate::stream_fragmenter::BuildFragmentGraphState;
31
32#[derive(Debug, Clone, PartialEq, Eq, Hash)]
33pub struct StreamOverWindow {
34 pub base: PlanBase<Stream>,
35 core: generic::OverWindow<PlanRef>,
36}
37
38impl StreamOverWindow {
39 pub fn new(core: generic::OverWindow<PlanRef>) -> Result<Self> {
40 assert!(core.funcs_have_same_partition_and_order());
41 reject_upsert_input!(core.input);
42
43 let input = &core.input;
44 let watermark_columns = WatermarkColumns::new();
45
46 let base = PlanBase::new_stream_with_core(
47 &core,
48 input.distribution().clone(),
49 StreamKind::Retract, false,
51 watermark_columns,
52 MonotonicityMap::new(), );
54
55 Ok(StreamOverWindow { base, core })
56 }
57
58 fn infer_state_table(&self) -> TableCatalog {
59 let mut tbl_builder = TableCatalogBuilder::default();
60
61 let out_schema = self.core.schema();
62 for field in out_schema.fields() {
63 tbl_builder.add_column(field);
64 }
65
66 let mut order_cols = HashSet::new();
67 for idx in self.core.partition_key_indices() {
68 if order_cols.insert(idx) {
69 tbl_builder.add_order_column(idx, OrderType::ascending());
70 }
71 }
72 let read_prefix_len_hint = tbl_builder.get_current_pk_len();
73 for o in self.core.order_key() {
74 if order_cols.insert(o.column_index) {
75 tbl_builder.add_order_column(o.column_index, o.order_type);
76 }
77 }
78 for &idx in self.core.input.expect_stream_key() {
79 if order_cols.insert(idx) {
80 tbl_builder.add_order_column(idx, OrderType::ascending());
81 }
82 }
83
84 let in_dist_key = self.core.input.distribution().dist_column_indices();
85 tbl_builder.build(in_dist_key.to_vec(), read_prefix_len_hint)
86 }
87
88 fn state_cleaning_enabled(&self) -> bool {
99 let input = &self.core.input;
100 let Some(first_order_key) = self.core.order_key().first() else {
101 return false;
102 };
103 input.append_only()
104 && self.core.window_functions().iter().all(|func| {
105 matches!(&func.frame.bounds, FrameBounds::Rows(bounds)
106 if !bounds.start.is_unbounded_preceding() && !bounds.end.is_unbounded_following())
107 })
108 && first_order_key.order_type.nulls_are_largest()
109 && input.watermark_columns().contains(first_order_key.column_index)
110 && !self
113 .core
114 .partition_key_indices()
115 .contains(&first_order_key.column_index)
116 }
117}
118
119impl_distill_by_unit!(StreamOverWindow, core, "StreamOverWindow");
120
121impl PlanTreeNodeUnary<Stream> for StreamOverWindow {
122 fn input(&self) -> PlanRef {
123 self.core.input.clone()
124 }
125
126 fn clone_with_input(&self, input: PlanRef) -> Self {
127 let mut core = self.core.clone();
128 core.input = input;
129 Self::new(core).unwrap()
130 }
131}
132impl_plan_tree_node_for_unary! { Stream, StreamOverWindow }
133
134impl StreamNode for StreamOverWindow {
135 fn to_stream_prost_body(&self, state: &mut BuildFragmentGraphState) -> PbNodeBody {
136 use risingwave_pb::stream_plan::*;
137
138 let calls = self
139 .core
140 .window_functions()
141 .iter()
142 .map(PlanWindowFunction::to_protobuf)
143 .collect();
144 let partition_by = self
145 .core
146 .partition_key_indices()
147 .into_iter()
148 .map(|idx| idx as _)
149 .collect();
150 let order_by = self
151 .core
152 .order_key()
153 .iter()
154 .copied()
155 .map(ColumnOrder::to_protobuf)
156 .collect();
157 let state_table = self
158 .infer_state_table()
159 .with_id(state.gen_table_id_wrapped())
160 .to_internal_table_prost();
161
162 PbNodeBody::OverWindow(Box::new(OverWindowNode {
163 calls,
164 partition_by,
165 order_by,
166 state_table: Some(state_table),
167
168 #[expect(deprecated)]
170 cache_policy: PbOverWindowCachePolicy::Unspecified as _,
171
172 enable_state_cleaning: self.state_cleaning_enabled(),
173 }))
174 }
175}
176
177impl ExprRewritable<Stream> for StreamOverWindow {}
178
179impl ExprVisitable for StreamOverWindow {}