risingwave_frontend/optimizer/plan_node/generic/
hop_window.rs1use std::num::NonZeroUsize;
16
17use itertools::Itertools;
18use pretty_xmlish::{Pretty, StrAssocArr};
19use risingwave_common::catalog::{Field, Schema};
20use risingwave_common::types::{DataType, Interval};
21use risingwave_common::util::column_index_mapping::ColIndexMapping;
22use risingwave_expr::ExprError;
23
24use super::super::utils::IndicesDisplay;
25use super::{GenericPlanNode, GenericPlanRef, impl_distill_unit_from_fields};
26use crate::error::Result;
27use crate::expr::{ExprImpl, ExprType, FunctionCall, InputRef, InputRefDisplay, Literal};
28use crate::optimizer::optimizer_context::OptimizerContextRef;
29use crate::optimizer::property::FunctionalDependencySet;
30use crate::utils::ColIndexMappingRewriteExt;
31
32#[derive(Debug, Clone, PartialEq, Eq, Hash)]
34pub struct HopWindow<PlanRef> {
35 pub input: PlanRef,
36 pub time_col: InputRef,
37 pub window_slide: Interval,
38 pub window_size: Interval,
39 pub window_offset: Interval,
40 pub output_indices: Vec<usize>,
48}
49
50impl<PlanRef: GenericPlanRef> GenericPlanNode for HopWindow<PlanRef> {
51 fn schema(&self) -> Schema {
52 let output_type = DataType::window_of(&self.time_col.data_type).unwrap();
53 let mut original_schema = self.input.schema().clone();
54 original_schema.fields.reserve_exact(2);
55 let window_start = Field::with_name(output_type.clone(), "window_start");
56 let window_end = Field::with_name(output_type, "window_end");
57 original_schema.fields.push(window_start);
58 original_schema.fields.push(window_end);
59 self.output_indices
60 .iter()
61 .map(|&idx| original_schema[idx].clone())
62 .collect()
63 }
64
65 fn stream_key(&self) -> Option<Vec<usize>> {
66 let window_start_index = self
67 .output_indices
68 .iter()
69 .position(|&idx| idx == self.input.schema().len());
70 let window_end_index = self
71 .output_indices
72 .iter()
73 .position(|&idx| idx == self.input.schema().len() + 1);
74 if window_start_index.is_none() && window_end_index.is_none() {
75 None
76 } else {
77 let mut pk = self
78 .input
79 .stream_key()?
80 .iter()
81 .filter_map(|&pk_idx| self.output_indices.iter().position(|&idx| idx == pk_idx))
82 .collect_vec();
83 if let Some(start_idx) = window_start_index {
84 pk.push(start_idx);
85 };
86 if let Some(end_idx) = window_end_index {
87 pk.push(end_idx);
88 };
89 Some(pk)
90 }
91 }
92
93 fn ctx(&self) -> OptimizerContextRef {
94 self.input.ctx()
95 }
96
97 fn functional_dependency(&self) -> FunctionalDependencySet {
98 let mut fd_set = self
99 .i2o_col_mapping()
100 .rewrite_functional_dependency_set(self.input.functional_dependency().clone());
101 let (start_idx_in_output, end_idx_in_output) = {
102 let internal2output = self.internal2output_col_mapping();
103 (
104 internal2output.try_map(self.internal_window_start_col_idx()),
105 internal2output.try_map(self.internal_window_end_col_idx()),
106 )
107 };
108 if let Some(start_idx) = start_idx_in_output
109 && let Some(end_idx) = end_idx_in_output
110 {
111 fd_set.add_functional_dependency_by_column_indices(&[start_idx], &[end_idx]);
112 fd_set.add_functional_dependency_by_column_indices(&[end_idx], &[start_idx]);
113 }
114 fd_set
115 }
116}
117
118impl<PlanRef: GenericPlanRef> HopWindow<PlanRef> {
119 pub fn clone_with_input<OtherPlanRef>(&self, input: OtherPlanRef) -> HopWindow<OtherPlanRef> {
120 HopWindow {
121 input,
122 time_col: self.time_col.clone(),
123 window_slide: self.window_slide,
124 window_size: self.window_size,
125 window_offset: self.window_offset,
126 output_indices: self.output_indices.clone(),
127 }
128 }
129
130 pub fn output_window_start_col_idx(&self) -> Option<usize> {
131 self.internal2output_col_mapping()
132 .try_map(self.internal_window_start_col_idx())
133 }
134
135 pub fn output_window_end_col_idx(&self) -> Option<usize> {
136 self.internal2output_col_mapping()
137 .try_map(self.internal_window_end_col_idx())
138 }
139
140 pub fn into_parts(self) -> (PlanRef, InputRef, Interval, Interval, Interval, Vec<usize>) {
141 (
142 self.input,
143 self.time_col,
144 self.window_slide,
145 self.window_size,
146 self.window_offset,
147 self.output_indices,
148 )
149 }
150
151 pub fn internal_window_start_col_idx(&self) -> usize {
152 self.input.schema().len()
153 }
154
155 pub fn internal_window_end_col_idx(&self) -> usize {
156 self.input.schema().len() + 1
157 }
158
159 pub fn o2i_col_mapping(&self) -> ColIndexMapping {
160 self.output2internal_col_mapping()
161 .composite(&self.internal2input_col_mapping())
162 }
163
164 pub fn i2o_col_mapping(&self) -> ColIndexMapping {
165 self.input2internal_col_mapping()
166 .composite(&self.internal2output_col_mapping())
167 }
168
169 pub fn internal_column_num(&self) -> usize {
170 self.input.schema().len() + 2
171 }
172
173 pub fn output2internal_col_mapping(&self) -> ColIndexMapping {
174 self.internal2output_col_mapping()
175 .inverse()
176 .expect("must be invertible")
177 }
178
179 pub fn internal2output_col_mapping(&self) -> ColIndexMapping {
180 ColIndexMapping::with_remaining_columns(&self.output_indices, self.internal_column_num())
181 }
182
183 pub fn input2internal_col_mapping(&self) -> ColIndexMapping {
184 ColIndexMapping::identity_or_none(self.input.schema().len(), self.internal_column_num())
185 }
186
187 pub fn internal2input_col_mapping(&self) -> ColIndexMapping {
188 ColIndexMapping::identity_or_none(self.internal_column_num(), self.input.schema().len())
189 }
190
191 pub fn derive_window_start_and_end_exprs(&self) -> Result<(Vec<ExprImpl>, Vec<ExprImpl>)> {
192 let Self {
193 window_size,
194 window_slide,
195 window_offset,
196 time_col,
197 ..
198 } = &self;
199 let units = window_size
200 .exact_div(window_slide)
201 .and_then(|x| NonZeroUsize::new(usize::try_from(x).ok()?))
202 .ok_or_else(|| ExprError::InvalidParam {
203 name: "window",
204 reason: format!(
205 "window_size {} cannot be divided by window_slide {}",
206 window_size, window_slide
207 )
208 .into(),
209 })?
210 .get();
211 let window_size_expr: ExprImpl =
212 Literal::new(Some((*window_size).into()), DataType::Interval).into();
213 let window_slide_expr: ExprImpl =
214 Literal::new(Some((*window_slide).into()), DataType::Interval).into();
215 let window_offset_expr: ExprImpl =
216 Literal::new(Some((*window_offset).into()), DataType::Interval).into();
217
218 let window_size_sub_slide = FunctionCall::new(
219 ExprType::Subtract,
220 vec![window_size_expr, window_slide_expr.clone()],
221 )?
222 .into();
223
224 let time_col_shifted = FunctionCall::new(
225 ExprType::Subtract,
226 vec![
227 ExprImpl::InputRef(Box::new(time_col.clone())),
228 window_size_sub_slide,
229 ],
230 )?
231 .into();
232
233 let hop_start: ExprImpl = FunctionCall::new(
234 ExprType::TumbleStart,
235 vec![time_col_shifted, window_slide_expr, window_offset_expr],
236 )?
237 .into();
238
239 let mut window_start_exprs = Vec::with_capacity(units);
240 let mut window_end_exprs = Vec::with_capacity(units);
241 for i in 0..units {
242 {
243 let window_start_offset =
244 window_slide
245 .checked_mul_int(i)
246 .ok_or_else(|| ExprError::InvalidParam {
247 name: "window",
248 reason: format!(
249 "window_slide {} cannot be multiplied by {}",
250 window_slide, i
251 )
252 .into(),
253 })?;
254 let window_start_offset_expr =
255 Literal::new(Some(window_start_offset.into()), DataType::Interval).into();
256 let window_start_expr = FunctionCall::new(
257 ExprType::Add,
258 vec![hop_start.clone(), window_start_offset_expr],
259 )?
260 .into();
261 window_start_exprs.push(window_start_expr);
262 }
263 {
264 let window_end_offset =
265 window_slide.checked_mul_int(i + units).ok_or_else(|| {
266 ExprError::InvalidParam {
267 name: "window",
268 reason: format!(
269 "window_slide {} cannot be multiplied by {}",
270 window_slide,
271 i + units
272 )
273 .into(),
274 }
275 })?;
276 let window_end_offset_expr =
277 Literal::new(Some(window_end_offset.into()), DataType::Interval).into();
278 let window_end_expr = FunctionCall::new(
279 ExprType::Add,
280 vec![hop_start.clone(), window_end_offset_expr],
281 )?
282 .into();
283 window_end_exprs.push(window_end_expr);
284 }
285 }
286 assert_eq!(window_start_exprs.len(), window_end_exprs.len());
287 Ok((window_start_exprs, window_end_exprs))
288 }
289
290 pub fn fields_pretty<'a>(&self) -> StrAssocArr<'a> {
291 let mut out = Vec::with_capacity(5);
292 let output_type = DataType::window_of(&self.time_col.data_type).unwrap();
293 out.push((
294 "time_col",
295 Pretty::display(&InputRefDisplay {
296 input_ref: &self.time_col,
297 input_schema: self.input.schema(),
298 }),
299 ));
300 out.push(("slide", Pretty::display(&self.window_slide)));
301 out.push(("size", Pretty::display(&self.window_size)));
302 if self
303 .output_indices
304 .iter()
305 .copied()
306 .eq(0..(self.input.schema().len() + 2))
308 {
309 out.push(("output", Pretty::from("all")));
310 } else {
311 let original_schema: Schema = self
312 .input
313 .schema()
314 .clone()
315 .into_fields()
316 .into_iter()
317 .chain([
318 Field::with_name(output_type.clone(), "window_start"),
319 Field::with_name(output_type, "window_end"),
320 ])
321 .collect();
322 let id = IndicesDisplay {
323 indices: &self.output_indices,
324 schema: &original_schema,
325 };
326 out.push(("output", id.distill()));
327 }
328 out
329 }
330}
331
332impl_distill_unit_from_fields!(HopWindow, GenericPlanRef);