datafusion_physical_expr/window/
sliding_aggregate.rs1use std::any::Any;
21use std::ops::Range;
22use std::sync::Arc;
23
24use crate::aggregate::AggregateFunctionExpr;
25use crate::window::window_expr::{
26 AggregateWindowExpr, WindowEvalContext, WindowFn, filter_array,
27};
28use crate::window::{
29 PartitionBatches, PartitionWindowAggStates, PlainAggregateWindowExpr, WindowExpr,
30};
31use crate::{PhysicalExpr, expressions::PhysicalSortExpr};
32
33use arrow::array::{ArrayRef, BooleanArray};
34use arrow::datatypes::FieldRef;
35use arrow::record_batch::RecordBatch;
36use datafusion_common::{Result, ScalarValue};
37use datafusion_expr::{Accumulator, WindowFrame};
38
39#[derive(Debug)]
44pub struct SlidingAggregateWindowExpr {
45 aggregate: Arc<AggregateFunctionExpr>,
46 partition_by: Vec<Arc<dyn PhysicalExpr>>,
47 order_by: Vec<PhysicalSortExpr>,
48 window_frame: Arc<WindowFrame>,
49 filter: Option<Arc<dyn PhysicalExpr>>,
50}
51
52impl SlidingAggregateWindowExpr {
53 pub fn new(
55 aggregate: Arc<AggregateFunctionExpr>,
56 partition_by: &[Arc<dyn PhysicalExpr>],
57 order_by: &[PhysicalSortExpr],
58 window_frame: Arc<WindowFrame>,
59 filter: Option<Arc<dyn PhysicalExpr>>,
60 ) -> Self {
61 Self {
62 aggregate,
63 partition_by: partition_by.to_vec(),
64 order_by: order_by.to_vec(),
65 window_frame,
66 filter,
67 }
68 }
69
70 pub fn get_aggregate_expr(&self) -> &AggregateFunctionExpr {
72 &self.aggregate
73 }
74}
75
76impl WindowExpr for SlidingAggregateWindowExpr {
82 fn as_any(&self) -> &dyn Any {
84 self
85 }
86
87 fn field(&self) -> Result<FieldRef> {
88 Ok(self.aggregate.field())
89 }
90
91 fn name(&self) -> &str {
92 self.aggregate.name()
93 }
94
95 fn expressions(&self) -> Vec<Arc<dyn PhysicalExpr>> {
96 self.aggregate.expressions()
97 }
98
99 fn evaluate(&self, batch: &RecordBatch) -> Result<ArrayRef> {
100 self.aggregate_evaluate(batch)
101 }
102
103 fn evaluate_stateful(
104 &self,
105 partition_batches: &PartitionBatches,
106 window_agg_state: &mut PartitionWindowAggStates,
107 eval_ctx: &WindowEvalContext<'_>,
108 ) -> Result<()> {
109 self.aggregate_evaluate_stateful(partition_batches, window_agg_state, eval_ctx)
110 }
111
112 fn partition_by(&self) -> &[Arc<dyn PhysicalExpr>] {
113 &self.partition_by
114 }
115
116 fn order_by(&self) -> &[PhysicalSortExpr] {
117 &self.order_by
118 }
119
120 fn get_window_frame(&self) -> &Arc<WindowFrame> {
121 &self.window_frame
122 }
123
124 fn get_reverse_expr(&self) -> Option<Arc<dyn WindowExpr>> {
125 self.aggregate.reverse_expr().map(|reverse_expr| {
126 let reverse_window_frame = self.window_frame.reverse();
127 if reverse_window_frame.is_ever_expanding() {
128 Arc::new(PlainAggregateWindowExpr::new(
129 Arc::new(reverse_expr),
130 &self.partition_by.clone(),
131 &self
132 .order_by
133 .iter()
134 .map(|e| e.reverse())
135 .collect::<Vec<_>>(),
136 Arc::new(self.window_frame.reverse()),
137 self.filter.clone(),
138 )) as _
139 } else {
140 Arc::new(SlidingAggregateWindowExpr::new(
141 Arc::new(reverse_expr),
142 &self.partition_by.clone(),
143 &self
144 .order_by
145 .iter()
146 .map(|e| e.reverse())
147 .collect::<Vec<_>>(),
148 Arc::new(self.window_frame.reverse()),
149 self.filter.clone(),
150 )) as _
151 }
152 })
153 }
154
155 fn uses_bounded_memory(&self) -> bool {
156 !self.window_frame.end_bound.is_unbounded()
157 }
158
159 fn with_new_expressions(
160 &self,
161 args: Vec<Arc<dyn PhysicalExpr>>,
162 partition_bys: Vec<Arc<dyn PhysicalExpr>>,
163 order_by_exprs: Vec<Arc<dyn PhysicalExpr>>,
164 ) -> Option<Arc<dyn WindowExpr>> {
165 debug_assert_eq!(self.order_by.len(), order_by_exprs.len());
166
167 let new_order_by = self
168 .order_by
169 .iter()
170 .zip(order_by_exprs)
171 .map(|(req, new_expr)| PhysicalSortExpr {
172 expr: new_expr,
173 options: req.options,
174 })
175 .collect();
176 Some(Arc::new(SlidingAggregateWindowExpr {
177 aggregate: self
178 .aggregate
179 .with_new_expressions(args, vec![])
180 .map(Arc::new)?,
181 partition_by: partition_bys,
182 order_by: new_order_by,
183 window_frame: Arc::clone(&self.window_frame),
184 filter: self.filter.clone(),
185 }))
186 }
187
188 fn create_window_fn(&self) -> Result<WindowFn> {
189 Ok(WindowFn::Aggregate(self.get_accumulator()?))
190 }
191}
192
193impl AggregateWindowExpr for SlidingAggregateWindowExpr {
194 fn get_accumulator(&self) -> Result<Box<dyn Accumulator>> {
195 self.aggregate.create_sliding_accumulator()
196 }
197
198 fn filter_expr(&self) -> Option<&Arc<dyn PhysicalExpr>> {
199 self.filter.as_ref()
200 }
201
202 fn get_aggregate_result_inside_range(
205 &self,
206 last_range: &Range<usize>,
207 cur_range: &Range<usize>,
208 value_slice: &[ArrayRef],
209 accumulator: &mut Box<dyn Accumulator>,
210 filter_mask: Option<&BooleanArray>,
211 ) -> Result<ScalarValue> {
212 if cur_range.start == cur_range.end {
213 let retract_bound = last_range.end - last_range.start;
217 if retract_bound > 0 {
218 let slice_mask =
219 filter_mask.map(|m| m.slice(last_range.start, retract_bound));
220 let retract: Vec<ArrayRef> = value_slice
221 .iter()
222 .map(|v| v.slice(last_range.start, retract_bound))
223 .map(|arr| match &slice_mask {
224 Some(m) => filter_array(&arr, m),
225 None => Ok(arr),
226 })
227 .collect::<Result<Vec<_>>>()?;
228 accumulator.retract_batch(&retract)?
229 }
230 self.aggregate
231 .default_value(self.aggregate.field().data_type())
232 } else {
233 let update_bound = cur_range.end - last_range.end;
235 if update_bound > 0 {
236 let slice_mask =
237 filter_mask.map(|m| m.slice(last_range.end, update_bound));
238 let update: Vec<ArrayRef> = value_slice
239 .iter()
240 .map(|v| v.slice(last_range.end, update_bound))
241 .map(|arr| match &slice_mask {
242 Some(m) => filter_array(&arr, m),
243 None => Ok(arr),
244 })
245 .collect::<Result<Vec<_>>>()?;
246 accumulator.update_batch(&update)?
247 }
248
249 let retract_bound = cur_range.start - last_range.start;
251 if retract_bound > 0 {
252 let slice_mask =
253 filter_mask.map(|m| m.slice(last_range.start, retract_bound));
254 let retract: Vec<ArrayRef> = value_slice
255 .iter()
256 .map(|v| v.slice(last_range.start, retract_bound))
257 .map(|arr| match &slice_mask {
258 Some(m) => filter_array(&arr, m),
259 None => Ok(arr),
260 })
261 .collect::<Result<Vec<_>>>()?;
262 accumulator.retract_batch(&retract)?
263 }
264 accumulator.evaluate()
265 }
266 }
267
268 fn is_constant_in_partition(&self) -> bool {
269 false
270 }
271}