1use crate::utils::{get_scalar_value_from_args, get_signed_integer};
21use arrow::array::UInt64Builder;
22use arrow::compute::{interleave, take};
23use arrow::datatypes::FieldRef;
24use datafusion_common::arrow::array::ArrayRef;
25use datafusion_common::arrow::datatypes::DataType;
26use datafusion_common::arrow::datatypes::Field;
27use datafusion_common::{DataFusionError, Result, ScalarValue, arrow_datafusion_err};
28use datafusion_doc::window_doc_sections::DOC_SECTION_ANALYTICAL;
29use datafusion_expr::{
30 Documentation, LimitEffect, Literal, PartitionEvaluator, ReversedUDWF, Signature,
31 TypeSignature, Volatility, WindowUDFImpl,
32};
33use datafusion_functions_window_common::expr::ExpressionArgs;
34use datafusion_functions_window_common::field::WindowUDFFieldArgs;
35use datafusion_functions_window_common::partition::PartitionEvaluatorArgs;
36use datafusion_physical_expr::expressions;
37use datafusion_physical_expr_common::physical_expr::PhysicalExpr;
38use std::cmp::min;
39use std::collections::VecDeque;
40use std::hash::Hash;
41use std::ops::Range;
42use std::sync::{Arc, LazyLock};
43
44get_or_init_udwf!(
45 Lag,
46 lag,
47 lag_udwf,
48 "Returns the row value that precedes the current row by a specified \
49 offset within partition. If no such row exists, then returns the \
50 default value.",
51 WindowShift::lag
52);
53get_or_init_udwf!(
54 Lead,
55 lead,
56 lead_udwf,
57 "Returns the value from a row that follows the current row by a \
58 specified offset within the partition. If no such row exists, then \
59 returns the default value.",
60 WindowShift::lead
61);
62
63pub fn lag(
70 arg: datafusion_expr::Expr,
71 shift_offset: Option<i64>,
72 default_value: Option<ScalarValue>,
73) -> datafusion_expr::Expr {
74 let shift_offset_lit = shift_offset
75 .map(|v| v.lit())
76 .unwrap_or(ScalarValue::Null.lit());
77 let default_lit = default_value.unwrap_or(ScalarValue::Null).lit();
78
79 lag_udwf().call(vec![arg, shift_offset_lit, default_lit])
80}
81
82pub fn lead(
89 arg: datafusion_expr::Expr,
90 shift_offset: Option<i64>,
91 default_value: Option<ScalarValue>,
92) -> datafusion_expr::Expr {
93 let shift_offset_lit = shift_offset
94 .map(|v| v.lit())
95 .unwrap_or(ScalarValue::Null.lit());
96 let default_lit = default_value.unwrap_or(ScalarValue::Null).lit();
97
98 lead_udwf().call(vec![arg, shift_offset_lit, default_lit])
99}
100
101#[derive(Debug, PartialEq, Eq, Hash)]
102pub enum WindowShiftKind {
103 Lag,
104 Lead,
105}
106
107impl WindowShiftKind {
108 fn name(&self) -> &'static str {
109 match self {
110 WindowShiftKind::Lag => "lag",
111 WindowShiftKind::Lead => "lead",
112 }
113 }
114
115 fn shift_offset(&self, value: Option<i64>) -> i64 {
119 match self {
120 WindowShiftKind::Lag => value.unwrap_or(1),
121 WindowShiftKind::Lead => value.map_or(-1, |v| v.wrapping_neg()),
122 }
123 }
124}
125
126#[derive(Debug, PartialEq, Eq, Hash)]
128pub struct WindowShift {
129 signature: Signature,
130 kind: WindowShiftKind,
131}
132
133impl WindowShift {
134 fn new(kind: WindowShiftKind) -> Self {
135 Self {
136 signature: Signature::one_of(
137 vec![
138 TypeSignature::Any(1),
139 TypeSignature::Any(2),
140 TypeSignature::Any(3),
141 ],
142 Volatility::Immutable,
143 )
144 .with_parameter_names(vec![
145 "expr".to_string(),
146 "offset".to_string(),
147 "default".to_string(),
148 ])
149 .expect("valid parameter names for lead/lag"),
150 kind,
151 }
152 }
153
154 pub fn lag() -> Self {
155 Self::new(WindowShiftKind::Lag)
156 }
157
158 pub fn lead() -> Self {
159 Self::new(WindowShiftKind::Lead)
160 }
161
162 pub fn kind(&self) -> &WindowShiftKind {
163 &self.kind
164 }
165}
166
167static LAG_DOCUMENTATION: LazyLock<Documentation> = LazyLock::new(|| {
168 Documentation::builder(DOC_SECTION_ANALYTICAL, "Returns value evaluated at the row that is offset rows before the \
169 current row within the partition; if there is no such row, instead return default \
170 (which must be of the same type as value).", "lag(expression, offset, default)")
171 .with_argument("expression", "Expression to operate on")
172 .with_argument("offset", "Integer. Specifies how many rows back \
173 the value of expression should be retrieved. Defaults to 1.")
174 .with_argument("default", "The default value if the offset is \
175 not within the partition. Must be of the same type as expression.")
176 .with_sql_example(r#"
177```sql
178-- Example usage of the lag window function:
179SELECT employee_id,
180 salary,
181 lag(salary, 1, 0) OVER (ORDER BY employee_id) AS prev_salary
182FROM employees;
183
184+-------------+--------+-------------+
185| employee_id | salary | prev_salary |
186+-------------+--------+-------------+
187| 1 | 30000 | 0 |
188| 2 | 50000 | 30000 |
189| 3 | 70000 | 50000 |
190| 4 | 60000 | 70000 |
191+-------------+--------+-------------+
192```
193"#)
194 .build()
195});
196
197fn get_lag_doc() -> &'static Documentation {
198 &LAG_DOCUMENTATION
199}
200
201static LEAD_DOCUMENTATION: LazyLock<Documentation> = LazyLock::new(|| {
202 Documentation::builder(DOC_SECTION_ANALYTICAL,
203 "Returns value evaluated at the row that is offset rows after the \
204 current row within the partition; if there is no such row, instead return default \
205 (which must be of the same type as value).",
206 "lead(expression, offset, default)")
207 .with_argument("expression", "Expression to operate on")
208 .with_argument("offset", "Integer. Specifies how many rows \
209 forward the value of expression should be retrieved. Defaults to 1.")
210 .with_argument("default", "The default value if the offset is \
211 not within the partition. Must be of the same type as expression.")
212 .with_sql_example(r#"
213```sql
214-- Example usage of lead window function:
215SELECT
216 employee_id,
217 department,
218 salary,
219 lead(salary, 1, 0) OVER (PARTITION BY department ORDER BY salary) AS next_salary
220FROM employees;
221
222+-------------+-------------+--------+--------------+
223| employee_id | department | salary | next_salary |
224+-------------+-------------+--------+--------------+
225| 1 | Sales | 30000 | 50000 |
226| 2 | Sales | 50000 | 70000 |
227| 3 | Sales | 70000 | 0 |
228| 4 | Engineering | 40000 | 60000 |
229| 5 | Engineering | 60000 | 0 |
230+-------------+-------------+--------+--------------+
231```
232"#)
233 .build()
234});
235
236fn get_lead_doc() -> &'static Documentation {
237 &LEAD_DOCUMENTATION
238}
239
240impl WindowUDFImpl for WindowShift {
241 fn name(&self) -> &str {
242 self.kind.name()
243 }
244
245 fn signature(&self) -> &Signature {
246 &self.signature
247 }
248
249 fn expressions(&self, expr_args: ExpressionArgs) -> Vec<Arc<dyn PhysicalExpr>> {
255 parse_expr(expr_args.input_exprs(), expr_args.input_fields())
256 .into_iter()
257 .collect::<Vec<_>>()
258 }
259
260 fn partition_evaluator(
261 &self,
262 partition_evaluator_args: PartitionEvaluatorArgs,
263 ) -> Result<Box<dyn PartitionEvaluator>> {
264 let shift_offset =
265 get_scalar_value_from_args(partition_evaluator_args.input_exprs(), 1)?
266 .map(|v| get_signed_integer(&v))
267 .map_or(Ok(None), |v| v.map(Some))
268 .map(|n| self.kind.shift_offset(n))
269 .map(|offset| {
270 if partition_evaluator_args.is_reversed() {
271 offset.wrapping_neg()
272 } else {
273 offset
274 }
275 })?;
276 let default_value = parse_default_value(
277 partition_evaluator_args.input_exprs(),
278 partition_evaluator_args.input_fields(),
279 )?;
280
281 Ok(Box::new(WindowShiftEvaluator {
282 shift_offset,
283 default_value,
284 ignore_nulls: partition_evaluator_args.ignore_nulls(),
285 non_null_offsets: VecDeque::new(),
286 }))
287 }
288
289 fn field(&self, field_args: WindowUDFFieldArgs) -> Result<FieldRef> {
290 let return_field = parse_expr_field(field_args.input_fields())?;
291
292 Ok(return_field
293 .as_ref()
294 .clone()
295 .with_name(field_args.name())
296 .into())
297 }
298
299 fn reverse_expr(&self) -> ReversedUDWF {
300 match self.kind {
301 WindowShiftKind::Lag => ReversedUDWF::Reversed(lag_udwf()),
302 WindowShiftKind::Lead => ReversedUDWF::Reversed(lead_udwf()),
303 }
304 }
305
306 fn documentation(&self) -> Option<&Documentation> {
307 match self.kind {
308 WindowShiftKind::Lag => Some(get_lag_doc()),
309 WindowShiftKind::Lead => Some(get_lead_doc()),
310 }
311 }
312
313 fn limit_effect(&self, args: &[Arc<dyn PhysicalExpr>]) -> LimitEffect {
314 if self.kind == WindowShiftKind::Lag {
315 return LimitEffect::None;
316 }
317 match args {
318 [_, expr, ..] => {
319 let Some(lit) = expr.downcast_ref::<expressions::Literal>() else {
320 return LimitEffect::Unknown;
321 };
322 let ScalarValue::Int64(Some(amount)) = lit.value() else {
323 return LimitEffect::Unknown; };
325 LimitEffect::Relative((*amount).max(0) as usize)
326 }
327 [_] => LimitEffect::Relative(1), _ => LimitEffect::Unknown, }
330 }
331}
332
333fn parse_expr(
346 input_exprs: &[Arc<dyn PhysicalExpr>],
347 input_fields: &[FieldRef],
348) -> Result<Arc<dyn PhysicalExpr>> {
349 assert!(!input_exprs.is_empty());
350 assert!(!input_fields.is_empty());
351
352 let expr = Arc::clone(input_exprs.first().unwrap());
353 let expr_field = input_fields.first().unwrap();
354
355 if !expr_field.data_type().is_null() {
357 return Ok(expr);
358 }
359
360 let default_value = get_scalar_value_from_args(input_exprs, 2)?;
361 default_value.map_or(Ok(expr), |value| {
362 ScalarValue::try_from(&value.data_type())
363 .map(|v| Arc::new(expressions::Literal::new(v)) as Arc<dyn PhysicalExpr>)
364 })
365}
366
367static NULL_FIELD: LazyLock<FieldRef> =
368 LazyLock::new(|| Field::new("value", DataType::Null, true).into());
369
370fn parse_expr_field(input_fields: &[FieldRef]) -> Result<FieldRef> {
375 assert!(!input_fields.is_empty());
376 let expr_field = input_fields.first().unwrap_or(&NULL_FIELD);
377
378 if !expr_field.data_type().is_null() {
380 return Ok(expr_field.as_ref().clone().with_nullable(true).into());
381 }
382
383 let default_value_field = input_fields.get(2).unwrap_or(&NULL_FIELD);
384 Ok(default_value_field
385 .as_ref()
386 .clone()
387 .with_nullable(true)
388 .into())
389}
390
391fn parse_default_value(
394 input_exprs: &[Arc<dyn PhysicalExpr>],
395 input_types: &[FieldRef],
396) -> Result<ScalarValue> {
397 let expr_field = parse_expr_field(input_types)?;
398 let unparsed = get_scalar_value_from_args(input_exprs, 2)?;
399
400 unparsed
401 .filter(|v| !v.data_type().is_null())
402 .map(|v| v.cast_to(expr_field.data_type()))
403 .unwrap_or_else(|| ScalarValue::try_from(expr_field.data_type()))
404}
405
406#[derive(Debug)]
407struct WindowShiftEvaluator {
408 shift_offset: i64,
409 default_value: ScalarValue,
410 ignore_nulls: bool,
411 non_null_offsets: VecDeque<usize>,
413}
414
415fn offset_magnitude(offset: i64) -> usize {
416 let offset = offset.unsigned_abs();
417 if offset > usize::MAX as u64 {
418 usize::MAX
419 } else {
420 offset as usize
421 }
422}
423
424enum ShiftIndexBuilder {
425 Take(UInt64Builder),
426 Interleave(Vec<(usize, usize)>),
427}
428
429impl ShiftIndexBuilder {
430 fn new(capacity: usize, default_is_null: bool) -> Self {
431 if default_is_null {
432 Self::Take(UInt64Builder::with_capacity(capacity))
433 } else {
434 Self::Interleave(Vec::with_capacity(capacity))
435 }
436 }
437
438 fn append_option(&mut self, index: Option<usize>) {
439 match self {
440 Self::Take(indices) => {
441 indices.append_option(index.map(|index| index as u64));
442 }
443 Self::Interleave(indices) => {
444 indices.push(index.map_or((1, 0), |index| (0, index)));
446 }
447 }
448 }
449
450 fn finish(
451 self,
452 array: &ArrayRef,
453 default_value: &ScalarValue,
454 ) -> Result<ArrayRef, DataFusionError> {
455 match self {
456 Self::Take(mut indices) => {
457 let indices = indices.finish();
458 take(array.as_ref(), &indices, None)
459 .map_err(|error| arrow_datafusion_err!(error))
460 }
461 Self::Interleave(indices) => {
462 let default = default_value.to_array_of_size(1)?;
463 interleave(&[array.as_ref(), default.as_ref()], &indices)
464 .map_err(|error| arrow_datafusion_err!(error))
465 }
466 }
467 }
468}
469
470impl WindowShiftEvaluator {
471 fn is_lag(&self) -> bool {
472 self.shift_offset > 0
474 }
475}
476
477fn evaluate_all_with_ignore_null(
479 array: &ArrayRef,
480 offset: i64,
481 default_value: &ScalarValue,
482 is_lag: bool,
483) -> Result<ArrayRef, DataFusionError> {
484 if offset == 0 {
485 return Ok(Arc::clone(array));
486 }
487
488 let Some(nulls) = array.nulls() else {
490 return shift_with_default_value(array, offset, default_value);
491 };
492
493 let shift = offset_magnitude(offset);
494 if shift >= array.len() {
495 return default_value.to_array_of_size(array.len());
496 }
497
498 let mut indices = ShiftIndexBuilder::new(array.len(), default_value.is_null());
499 if is_lag {
500 let mut preceding = VecDeque::new();
501 for index in 0..array.len() {
502 let result_index = if preceding.len() == shift {
503 preceding.front().copied()
504 } else {
505 None
506 };
507 indices.append_option(result_index);
508
509 if nulls.is_valid(index) {
510 if preceding.len() == shift {
511 preceding.pop_front();
512 }
513 preceding.push_back(index);
514 }
515 }
516 } else {
517 let mut following = VecDeque::new();
518 let mut next_index = 0;
519 for index in 0..array.len() {
520 while following.front().is_some_and(|next| *next <= index) {
521 following.pop_front();
522 }
523 next_index = next_index.max(index.saturating_add(1));
524 while following.len() < shift && next_index < array.len() {
525 if nulls.is_valid(next_index) {
526 following.push_back(next_index);
527 }
528 next_index += 1;
529 }
530 indices.append_option(following.get(shift - 1).copied());
531 }
532 }
533
534 indices.finish(array, default_value)
535}
536fn shift_with_default_value(
538 array: &ArrayRef,
539 offset: i64,
540 default_value: &ScalarValue,
541) -> Result<ArrayRef> {
542 use datafusion_common::arrow::compute::concat;
543
544 let value_len = array.len() as i64;
545 if offset == 0 {
546 Ok(Arc::clone(array))
547 } else if offset == i64::MIN || offset.abs() >= value_len {
548 default_value.to_array_of_size(value_len as usize)
549 } else {
550 let slice_offset = (-offset).clamp(0, value_len) as usize;
551 let length = array.len() - offset.unsigned_abs() as usize;
552 let slice = array.slice(slice_offset, length);
553
554 let nulls = offset.unsigned_abs() as usize;
556 let default_values = default_value.to_array_of_size(nulls)?;
557
558 if offset > 0 {
560 concat(&[default_values.as_ref(), slice.as_ref()])
561 .map_err(|e| arrow_datafusion_err!(e))
562 } else {
563 concat(&[slice.as_ref(), default_values.as_ref()])
564 .map_err(|e| arrow_datafusion_err!(e))
565 }
566 }
567}
568
569impl PartitionEvaluator for WindowShiftEvaluator {
570 fn get_range(&self, idx: usize, n_rows: usize) -> Result<Range<usize>> {
571 let offset = offset_magnitude(self.shift_offset);
572
573 if self.is_lag() {
574 let start = if self.non_null_offsets.len() == offset {
575 let offset: usize = self.non_null_offsets.iter().sum();
577 idx.saturating_sub(offset)
578 } else if !self.ignore_nulls {
579 idx.saturating_sub(offset)
580 } else {
581 0
582 };
583 let end = idx + 1;
584 Ok(Range { start, end })
585 } else {
586 let end = if self.non_null_offsets.len() == offset {
587 let offset: usize = self.non_null_offsets.iter().sum();
589 min(idx.saturating_add(offset).saturating_add(1), n_rows)
590 } else if !self.ignore_nulls {
591 min(idx.saturating_add(offset), n_rows)
592 } else {
593 n_rows
594 };
595 Ok(Range { start: idx, end })
596 }
597 }
598
599 fn is_causal(&self) -> bool {
600 self.is_lag()
602 }
603
604 fn evaluate(
605 &mut self,
606 values: &[ArrayRef],
607 range: &Range<usize>,
608 ) -> Result<ScalarValue> {
609 let array = &values[0];
610 let len = array.len();
611
612 let i = if self.is_lag() {
614 range
615 .end
616 .checked_sub(1)
617 .and_then(|end| (end as i64).checked_sub(self.shift_offset))
618 .and_then(|value| usize::try_from(value).ok())
619 } else {
620 (range.start as i64)
622 .checked_sub(self.shift_offset)
623 .and_then(|value| usize::try_from(value).ok())
624 };
625 let mut idx: Option<usize> = i.filter(|i| *i < len);
626
627 if self.ignore_nulls && self.is_lag() {
630 let shift_offset = offset_magnitude(self.shift_offset);
633 idx = if self.non_null_offsets.len() == shift_offset {
634 let total_offset: usize = self.non_null_offsets.iter().sum();
635 Some(range.end - 1 - total_offset)
636 } else {
637 None
638 };
639
640 if array.is_valid(range.end - 1) {
642 self.non_null_offsets.push_back(1);
644 if self.non_null_offsets.len() > shift_offset {
645 self.non_null_offsets.pop_front();
647 }
648 } else if !self.non_null_offsets.is_empty() {
649 let end_idx = self.non_null_offsets.len() - 1;
651 self.non_null_offsets[end_idx] += 1;
652 }
653 } else if self.ignore_nulls && !self.is_lag() {
654 let non_null_row_count = offset_magnitude(self.shift_offset);
657
658 if self.non_null_offsets.is_empty() {
659 let mut offset_val = 1;
661 for idx in range.start + 1..range.end {
662 if array.is_valid(idx) {
663 self.non_null_offsets.push_back(offset_val);
664 offset_val = 1;
665 } else {
666 offset_val += 1;
667 }
668 if self.non_null_offsets.len() == non_null_row_count.saturating_add(1)
671 {
672 break;
673 }
674 }
675 } else if range.end < len && array.is_valid(range.end) {
676 if array.is_valid(range.end) {
678 self.non_null_offsets.push_back(1);
680 } else {
681 let last_idx = self.non_null_offsets.len() - 1;
683 self.non_null_offsets[last_idx] += 1;
684 }
685 }
686
687 idx = if self.non_null_offsets.len() >= non_null_row_count {
689 let total_offset: usize =
690 self.non_null_offsets.iter().take(non_null_row_count).sum();
691 Some(range.start + total_offset)
692 } else {
693 None
694 };
695 if !self.non_null_offsets.is_empty() {
698 self.non_null_offsets[0] -= 1;
699 if self.non_null_offsets[0] == 0 {
700 self.non_null_offsets.pop_front();
702 }
703 }
704 }
705
706 #[expect(clippy::unnecessary_unwrap)]
712 if !(idx.is_none() || (self.ignore_nulls && array.is_null(idx.unwrap()))) {
713 ScalarValue::try_from_array(array, idx.unwrap())
714 } else {
715 Ok(self.default_value.clone())
716 }
717 }
718
719 fn evaluate_all(
720 &mut self,
721 values: &[ArrayRef],
722 _num_rows: usize,
723 ) -> Result<ArrayRef> {
724 let value = &values[0];
726 if !self.ignore_nulls {
727 shift_with_default_value(value, self.shift_offset, &self.default_value)
728 } else {
729 evaluate_all_with_ignore_null(
730 value,
731 self.shift_offset,
732 &self.default_value,
733 self.is_lag(),
734 )
735 }
736 }
737
738 fn supports_bounded_execution(&self) -> bool {
739 true
740 }
741}
742
743#[cfg(test)]
744mod tests {
745 use super::*;
746 use arrow::array::*;
747 use arrow::datatypes::Int8Type;
748 use datafusion_common::cast::{as_dictionary_array, as_int32_array, as_string_array};
749 use datafusion_physical_expr::expressions::{Column, Literal};
750
751 fn test_i32_result(
752 expr: WindowShift,
753 partition_evaluator_args: PartitionEvaluatorArgs,
754 expected: Int32Array,
755 ) -> Result<()> {
756 let arr: ArrayRef = Arc::new(Int32Array::from(vec![1, -2, 3, -4, 5, -6, 7, 8]));
757 let values = vec![arr];
758 let num_rows = values.len();
759 let result = expr
760 .partition_evaluator(partition_evaluator_args)?
761 .evaluate_all(&values, num_rows)?;
762 let result = as_int32_array(&result)?;
763 assert_eq!(expected, *result);
764 Ok(())
765 }
766
767 #[test]
768 fn lead_lag_get_range() -> Result<()> {
769 let lag_fn = WindowShiftEvaluator {
771 shift_offset: 2,
772 default_value: ScalarValue::Null,
773 ignore_nulls: false,
774 non_null_offsets: Default::default(),
775 };
776 assert_eq!(lag_fn.get_range(6, 10)?, Range { start: 4, end: 7 });
777 assert_eq!(lag_fn.get_range(0, 10)?, Range { start: 0, end: 1 });
778
779 let lag_fn = WindowShiftEvaluator {
781 shift_offset: 2,
782 default_value: ScalarValue::Null,
783 ignore_nulls: true,
784 non_null_offsets: vec![2, 2].into(), };
787 assert_eq!(lag_fn.get_range(6, 10)?, Range { start: 2, end: 7 });
788
789 let lead_fn = WindowShiftEvaluator {
791 shift_offset: -2,
792 default_value: ScalarValue::Null,
793 ignore_nulls: false,
794 non_null_offsets: Default::default(),
795 };
796 assert_eq!(lead_fn.get_range(6, 10)?, Range { start: 6, end: 8 });
797 assert_eq!(lead_fn.get_range(9, 10)?, Range { start: 9, end: 10 });
798
799 let lead_fn = WindowShiftEvaluator {
801 shift_offset: -2,
802 default_value: ScalarValue::Null,
803 ignore_nulls: true,
804 non_null_offsets: vec![2, 2].into(),
806 };
807 assert_eq!(lead_fn.get_range(4, 10)?, Range { start: 4, end: 9 });
808
809 Ok(())
810 }
811
812 #[test]
813 fn test_lead_window_shift() -> Result<()> {
814 let expr = Arc::new(Column::new("c3", 0)) as Arc<dyn PhysicalExpr>;
815
816 test_i32_result(
817 WindowShift::lead(),
818 PartitionEvaluatorArgs::new(
819 &[expr],
820 &[Field::new("f", DataType::Int32, true).into()],
821 false,
822 false,
823 ),
824 [
825 Some(-2),
826 Some(3),
827 Some(-4),
828 Some(5),
829 Some(-6),
830 Some(7),
831 Some(8),
832 None,
833 ]
834 .iter()
835 .collect::<Int32Array>(),
836 )
837 }
838
839 #[test]
840 fn test_lag_window_shift() -> Result<()> {
841 let expr = Arc::new(Column::new("c3", 0)) as Arc<dyn PhysicalExpr>;
842
843 test_i32_result(
844 WindowShift::lag(),
845 PartitionEvaluatorArgs::new(
846 &[expr],
847 &[Field::new("f", DataType::Int32, true).into()],
848 false,
849 false,
850 ),
851 [
852 None,
853 Some(1),
854 Some(-2),
855 Some(3),
856 Some(-4),
857 Some(5),
858 Some(-6),
859 Some(7),
860 ]
861 .iter()
862 .collect::<Int32Array>(),
863 )
864 }
865
866 #[test]
867 fn test_lag_with_default() -> Result<()> {
868 let expr = Arc::new(Column::new("c3", 0)) as Arc<dyn PhysicalExpr>;
869 let shift_offset =
870 Arc::new(Literal::new(ScalarValue::Int32(Some(1)))) as Arc<dyn PhysicalExpr>;
871 let default_value = Arc::new(Literal::new(ScalarValue::Int32(Some(100))))
872 as Arc<dyn PhysicalExpr>;
873
874 let input_exprs = &[expr, shift_offset, default_value];
875 let input_fields = [DataType::Int32, DataType::Int32, DataType::Int32]
876 .into_iter()
877 .map(|d| Field::new("f", d, true))
878 .map(Arc::new)
879 .collect::<Vec<_>>();
880
881 test_i32_result(
882 WindowShift::lag(),
883 PartitionEvaluatorArgs::new(input_exprs, &input_fields, false, false),
884 [
885 Some(100),
886 Some(1),
887 Some(-2),
888 Some(3),
889 Some(-4),
890 Some(5),
891 Some(-6),
892 Some(7),
893 ]
894 .iter()
895 .collect::<Int32Array>(),
896 )
897 }
898
899 #[test]
900 fn test_evaluate_all_with_ignore_null() -> Result<()> {
901 let input: ArrayRef = Arc::new(Int32Array::from(vec![
902 None,
903 Some(10),
904 None,
905 Some(20),
906 Some(30),
907 None,
908 ]));
909
910 let cases = [
911 (
912 1,
913 ScalarValue::Int32(None),
914 Int32Array::from(vec![
915 None,
916 None,
917 Some(10),
918 Some(10),
919 Some(20),
920 Some(30),
921 ]),
922 ),
923 (
924 -1,
925 ScalarValue::Int32(None),
926 Int32Array::from(vec![
927 Some(10),
928 Some(20),
929 Some(20),
930 Some(30),
931 None,
932 None,
933 ]),
934 ),
935 (
936 2,
937 ScalarValue::Int32(Some(-1)),
938 Int32Array::from(vec![
939 Some(-1),
940 Some(-1),
941 Some(-1),
942 Some(-1),
943 Some(10),
944 Some(20),
945 ]),
946 ),
947 (
948 -2,
949 ScalarValue::Int32(Some(-1)),
950 Int32Array::from(vec![
951 Some(20),
952 Some(30),
953 Some(30),
954 Some(-1),
955 Some(-1),
956 Some(-1),
957 ]),
958 ),
959 (
960 0,
961 ScalarValue::Int32(Some(-1)),
962 Int32Array::from(vec![None, Some(10), None, Some(20), Some(30), None]),
963 ),
964 ];
965
966 for (offset, default_value, expected) in cases {
967 let actual = evaluate_all_with_ignore_null(
968 &input,
969 offset,
970 &default_value,
971 offset > 0,
972 )?;
973 assert_eq!(expected, *as_int32_array(&actual)?);
974 }
975 Ok(())
976 }
977
978 #[test]
979 fn test_ignore_nulls_dictionary_with_bounded_keys() -> Result<()> {
980 let keys =
981 Int8Array::from_iter(std::iter::once(None).chain((0_i8..=127).map(Some)));
982 let values =
983 StringArray::from_iter_values((0..128).map(|index| format!("value-{index}")));
984 let input: ArrayRef = Arc::new(DictionaryArray::<Int8Type>::try_new(
985 keys,
986 Arc::new(values),
987 )?);
988 let default_value = ScalarValue::Dictionary(
989 Box::new(DataType::Int8),
990 Box::new(ScalarValue::Utf8(Some("default".to_string()))),
991 );
992
993 let actual = evaluate_all_with_ignore_null(&input, 1, &default_value, true)?;
994 let actual = as_dictionary_array::<Int8Type>(actual.as_ref())?;
995 let values = as_string_array(actual.values().as_ref())?;
996
997 assert_eq!(actual.len(), 129);
998 assert_eq!(values.len(), 128);
999 for index in 0..2 {
1000 let key = actual.key(index).expect("non-null default");
1001 assert_eq!(values.value(key), "default");
1002 }
1003 for index in 2..actual.len() {
1004 let key = actual.key(index).expect("selected value");
1005 assert_eq!(values.value(key), format!("value-{}", index - 2));
1006 }
1007 Ok(())
1008 }
1009
1010 #[test]
1011 fn test_ignore_nulls_without_null_bitmap() -> Result<()> {
1012 let input = Int32Array::from(vec![1, 2, 3]);
1013 assert!(input.nulls().is_none());
1014 let input: ArrayRef = Arc::new(input);
1015
1016 for (offset, expected) in [
1017 (1, Int32Array::from(vec![None, Some(1), Some(2)])),
1018 (-1, Int32Array::from(vec![Some(2), Some(3), None])),
1019 ] {
1020 let actual = evaluate_all_with_ignore_null(
1021 &input,
1022 offset,
1023 &ScalarValue::Int32(None),
1024 offset > 0,
1025 )?;
1026 assert_eq!(expected, *as_int32_array(&actual)?);
1027 }
1028 Ok(())
1029 }
1030}