1use crate::utils::{get_scalar_value_from_args, get_signed_integer};
21
22use arrow::buffer::NullBuffer;
23use arrow::datatypes::FieldRef;
24use datafusion_common::arrow::array::ArrayRef;
25use datafusion_common::arrow::datatypes::{DataType, Field};
26use datafusion_common::{Result, ScalarValue, exec_datafusion_err, exec_err};
27use datafusion_doc::window_doc_sections::DOC_SECTION_ANALYTICAL;
28use datafusion_expr::window_state::WindowAggState;
29use datafusion_expr::{
30 Documentation, LimitEffect, Literal, PartitionEvaluator, ReversedUDWF, Signature,
31 TypeSignature, Volatility, WindowUDFImpl,
32};
33use datafusion_functions_window_common::field;
34use datafusion_functions_window_common::partition::PartitionEvaluatorArgs;
35use datafusion_physical_expr_common::physical_expr::PhysicalExpr;
36use field::WindowUDFFieldArgs;
37use std::cmp::Ordering;
38use std::fmt::Debug;
39use std::hash::Hash;
40use std::ops::Range;
41use std::sync::{Arc, LazyLock};
42
43define_udwf_and_expr!(
44 First,
45 first_value,
46 [arg],
47 first_value_udwf,
48 "Returns the first value in the window frame",
49 NthValue::first
50);
51define_udwf_and_expr!(
52 Last,
53 last_value,
54 [arg],
55 last_value_udwf,
56 "Returns the last value in the window frame",
57 NthValue::last
58);
59get_or_init_udwf!(
60 NthValue,
61 nth_value,
62 nth_value_udwf,
63 "Returns the nth value in the window frame",
64 NthValue::nth
65);
66
67pub fn nth_value(arg: datafusion_expr::Expr, n: i64) -> datafusion_expr::Expr {
69 nth_value_udwf().call(vec![arg, n.lit()])
70}
71
72#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
74pub enum NthValueKind {
75 First,
76 Last,
77 Nth,
78}
79
80impl NthValueKind {
81 fn name(&self) -> &'static str {
82 match self {
83 NthValueKind::First => "first_value",
84 NthValueKind::Last => "last_value",
85 NthValueKind::Nth => "nth_value",
86 }
87 }
88}
89
90#[derive(Debug, PartialEq, Eq, Hash)]
91pub struct NthValue {
92 signature: Signature,
93 kind: NthValueKind,
94}
95
96impl NthValue {
97 pub fn new(kind: NthValueKind) -> Self {
99 Self {
100 signature: Signature::one_of(
101 vec![
102 TypeSignature::Nullary,
103 TypeSignature::Any(1),
104 TypeSignature::Any(2),
105 ],
106 Volatility::Immutable,
107 ),
108 kind,
109 }
110 }
111
112 pub fn first() -> Self {
113 Self::new(NthValueKind::First)
114 }
115
116 pub fn last() -> Self {
117 Self::new(NthValueKind::Last)
118 }
119 pub fn nth() -> Self {
120 Self::new(NthValueKind::Nth)
121 }
122
123 pub fn kind(&self) -> &NthValueKind {
124 &self.kind
125 }
126}
127
128fn validate_nth_value_n(n: i64) -> Result<i64> {
129 if n == i64::MIN {
130 return exec_err!("The second argument of nth_value must not be i64::MIN");
131 }
132
133 Ok(n)
134}
135
136static FIRST_VALUE_DOCUMENTATION: LazyLock<Documentation> = LazyLock::new(|| {
137 Documentation::builder(
138 DOC_SECTION_ANALYTICAL,
139 "Returns value evaluated at the row that is the first row of the window \
140 frame.",
141 "first_value(expression)",
142 )
143 .with_argument("expression", "Expression to operate on")
144 .with_sql_example(
145 r#"
146```sql
147-- Example usage of the first_value window function:
148SELECT department,
149 employee_id,
150 salary,
151 first_value(salary) OVER (PARTITION BY department ORDER BY salary DESC) AS top_salary
152FROM employees;
153
154+-------------+-------------+--------+------------+
155| department | employee_id | salary | top_salary |
156+-------------+-------------+--------+------------+
157| Sales | 1 | 70000 | 70000 |
158| Sales | 2 | 50000 | 70000 |
159| Sales | 3 | 30000 | 70000 |
160| Engineering | 4 | 90000 | 90000 |
161| Engineering | 5 | 80000 | 90000 |
162+-------------+-------------+--------+------------+
163```
164"#,
165 )
166 .build()
167});
168
169fn get_first_value_doc() -> &'static Documentation {
170 &FIRST_VALUE_DOCUMENTATION
171}
172
173static LAST_VALUE_DOCUMENTATION: LazyLock<Documentation> = LazyLock::new(|| {
174 Documentation::builder(
175 DOC_SECTION_ANALYTICAL,
176 "Returns value evaluated at the row that is the last row of the window \
177 frame.",
178 "last_value(expression)",
179 )
180 .with_argument("expression", "Expression to operate on")
181 .with_sql_example(r#"```sql
182-- SQL example of last_value:
183SELECT department,
184 employee_id,
185 salary,
186 last_value(salary) OVER (PARTITION BY department ORDER BY salary) AS running_last_salary
187FROM employees;
188
189+-------------+-------------+--------+---------------------+
190| department | employee_id | salary | running_last_salary |
191+-------------+-------------+--------+---------------------+
192| Sales | 1 | 30000 | 30000 |
193| Sales | 2 | 50000 | 50000 |
194| Sales | 3 | 70000 | 70000 |
195| Engineering | 4 | 40000 | 40000 |
196| Engineering | 5 | 60000 | 60000 |
197+-------------+-------------+--------+---------------------+
198```
199"#)
200 .build()
201});
202
203fn get_last_value_doc() -> &'static Documentation {
204 &LAST_VALUE_DOCUMENTATION
205}
206
207static NTH_VALUE_DOCUMENTATION: LazyLock<Documentation> = LazyLock::new(|| {
208 Documentation::builder(
209 DOC_SECTION_ANALYTICAL,
210 "Returns the value evaluated at the nth row of the window frame \
211 (counting from 1). Returns NULL if no such row exists.",
212 "nth_value(expression, n)",
213 )
214 .with_argument(
215 "expression",
216 "The column from which to retrieve the nth value.",
217 )
218 .with_argument(
219 "n",
220 "Integer. Specifies the row number (starting from 1) in the window frame.",
221 )
222 .with_sql_example(
223 r#"
224```sql
225-- Sample employees table:
226CREATE TABLE employees (id INT, salary INT);
227INSERT INTO employees (id, salary) VALUES
228(1, 30000),
229(2, 40000),
230(3, 50000),
231(4, 60000),
232(5, 70000);
233
234-- Example usage of nth_value:
235SELECT nth_value(salary, 2) OVER (
236 ORDER BY salary
237 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
238) AS nth_value
239FROM employees;
240
241+-----------+
242| nth_value |
243+-----------+
244| 40000 |
245| 40000 |
246| 40000 |
247| 40000 |
248| 40000 |
249+-----------+
250```
251"#,
252 )
253 .build()
254});
255
256fn get_nth_value_doc() -> &'static Documentation {
257 &NTH_VALUE_DOCUMENTATION
258}
259
260impl WindowUDFImpl for NthValue {
261 fn name(&self) -> &str {
262 self.kind.name()
263 }
264
265 fn signature(&self) -> &Signature {
266 &self.signature
267 }
268
269 fn partition_evaluator(
270 &self,
271 partition_evaluator_args: PartitionEvaluatorArgs,
272 ) -> Result<Box<dyn PartitionEvaluator>> {
273 let state = NthValueState {
274 finalized_result: None,
275 kind: self.kind,
276 };
277
278 if self.kind != NthValueKind::Nth {
279 return Ok(Box::new(NthValueEvaluator {
280 state,
281 ignore_nulls: partition_evaluator_args.ignore_nulls(),
282 n: 0,
283 }));
284 }
285
286 let n = match get_scalar_value_from_args(
287 partition_evaluator_args.input_exprs(),
288 1,
289 )
290 .map_err(|_e| {
291 exec_datafusion_err!(
292 "Expected a signed integer literal for the second argument of nth_value"
293 )
294 })?
295 .map(|v| get_signed_integer(&v))
296 {
297 Some(Ok(n)) => {
298 let n = validate_nth_value_n(n)?;
299 if partition_evaluator_args.is_reversed() {
300 -n
301 } else {
302 n
303 }
304 }
305 _ => {
306 return exec_err!(
307 "Expected a signed integer literal for the second argument of nth_value"
308 );
309 }
310 };
311
312 Ok(Box::new(NthValueEvaluator {
313 state,
314 ignore_nulls: partition_evaluator_args.ignore_nulls(),
315 n,
316 }))
317 }
318
319 fn field(&self, field_args: WindowUDFFieldArgs) -> Result<FieldRef> {
320 let input_field =
321 field_args
322 .input_fields()
323 .first()
324 .cloned()
325 .unwrap_or_else(|| {
326 Arc::new(Field::new(field_args.name(), DataType::Null, true))
327 });
328
329 Ok(input_field
331 .as_ref()
332 .clone()
333 .with_name(field_args.name())
334 .with_nullable(true)
335 .into())
336 }
337
338 fn reverse_expr(&self) -> ReversedUDWF {
339 match self.kind {
340 NthValueKind::First => ReversedUDWF::Reversed(last_value_udwf()),
341 NthValueKind::Last => ReversedUDWF::Reversed(first_value_udwf()),
342 NthValueKind::Nth => ReversedUDWF::Reversed(nth_value_udwf()),
343 }
344 }
345
346 fn documentation(&self) -> Option<&Documentation> {
347 match self.kind {
348 NthValueKind::First => Some(get_first_value_doc()),
349 NthValueKind::Last => Some(get_last_value_doc()),
350 NthValueKind::Nth => Some(get_nth_value_doc()),
351 }
352 }
353
354 fn limit_effect(&self, _args: &[Arc<dyn PhysicalExpr>]) -> LimitEffect {
355 LimitEffect::None }
357}
358
359#[derive(Debug, Clone)]
360pub struct NthValueState {
361 pub finalized_result: Option<ScalarValue>,
370 pub kind: NthValueKind,
371}
372
373#[derive(Debug)]
374pub(crate) struct NthValueEvaluator {
375 state: NthValueState,
376 ignore_nulls: bool,
377 n: i64,
378}
379
380impl PartitionEvaluator for NthValueEvaluator {
381 fn memoize(&mut self, state: &mut WindowAggState) -> Result<()> {
387 let out = &state.out_col;
388 let size = out.len();
389 if self.ignore_nulls {
390 match self.state.kind {
391 NthValueKind::First => {
393 if let Some(nulls) = out.nulls() {
394 if self.state.finalized_result.is_none() {
395 if let Some(valid_index) = nulls.valid_indices().next() {
396 let result =
397 ScalarValue::try_from_array(out, valid_index)?;
398 self.state.finalized_result = Some(result);
399 } else {
400 }
402 }
403 if state.window_frame_range.start < state.window_frame_range.end {
404 state.window_frame_range.start =
405 state.window_frame_range.end - 1;
406 }
407 return Ok(());
408 } else {
409 }
411 }
412 NthValueKind::Last | NthValueKind::Nth => return Ok(()),
414 }
415 }
416 let mut buffer_size = 1;
417 let (is_prunable, is_reverse_direction) = match self.state.kind {
419 NthValueKind::First => {
420 let n_range =
421 state.window_frame_range.end - state.window_frame_range.start;
422 (n_range > 0 && size > 0, false)
423 }
424 NthValueKind::Last => (true, true),
425 NthValueKind::Nth => {
426 let n_range =
427 state.window_frame_range.end - state.window_frame_range.start;
428 match self.n.cmp(&0) {
429 Ordering::Greater => (
430 n_range >= (self.n as usize) && size > (self.n as usize),
431 false,
432 ),
433 Ordering::Less => {
434 let reverse_index = (-self.n) as usize;
435 buffer_size = reverse_index;
436 (n_range >= reverse_index, true)
438 }
439 Ordering::Equal => (false, false),
440 }
441 }
442 };
443 if is_prunable {
444 if self.state.finalized_result.is_none() && !is_reverse_direction {
445 let result = ScalarValue::try_from_array(out, size - 1)?;
446 self.state.finalized_result = Some(result);
447 }
448 state.window_frame_range.start =
449 state.window_frame_range.end.saturating_sub(buffer_size);
450 }
451 Ok(())
452 }
453
454 fn evaluate(
455 &mut self,
456 values: &[ArrayRef],
457 range: &Range<usize>,
458 ) -> Result<ScalarValue> {
459 if let Some(ref result) = self.state.finalized_result {
460 Ok(result.clone())
461 } else {
462 let arr = &values[0];
464 let n_range = range.end - range.start;
465 if n_range == 0 {
466 return ScalarValue::try_from(arr.data_type());
468 }
469 match self.valid_index(arr, range) {
470 Some(index) => ScalarValue::try_from_array(arr, index),
471 None => ScalarValue::try_from(arr.data_type()),
472 }
473 }
474 }
475
476 fn supports_bounded_execution(&self) -> bool {
477 true
478 }
479
480 fn uses_window_frame(&self) -> bool {
481 true
482 }
483}
484
485impl NthValueEvaluator {
486 fn valid_index(&self, array: &ArrayRef, range: &Range<usize>) -> Option<usize> {
487 let n_range = range.end - range.start;
488 if self.ignore_nulls {
489 let slice = array.slice(range.start, n_range);
491 if let Some(nulls) = slice.nulls()
492 && nulls.null_count() > 0
493 {
494 return self.valid_index_with_nulls(nulls, range.start);
495 }
496 }
497 match self.state.kind {
499 NthValueKind::First => Some(range.start),
500 NthValueKind::Last => Some(range.end - 1),
501 NthValueKind::Nth => match self.n.cmp(&0) {
502 Ordering::Greater => {
503 let index = (self.n as usize) - 1;
505 if index >= n_range {
506 None
508 } else {
509 Some(range.start + index)
510 }
511 }
512 Ordering::Less => {
513 let reverse_index = (-self.n) as usize;
514 if n_range < reverse_index {
515 None
517 } else {
518 Some(range.end - reverse_index)
519 }
520 }
521 Ordering::Equal => None,
522 },
523 }
524 }
525
526 fn valid_index_with_nulls(&self, nulls: &NullBuffer, offset: usize) -> Option<usize> {
527 match self.state.kind {
528 NthValueKind::First => nulls.valid_indices().next().map(|idx| idx + offset),
529 NthValueKind::Last => nulls.valid_indices().last().map(|idx| idx + offset),
530 NthValueKind::Nth => {
531 match self.n.cmp(&0) {
532 Ordering::Greater => {
533 let index = (self.n as usize) - 1;
535 nulls.valid_indices().nth(index).map(|idx| idx + offset)
536 }
537 Ordering::Less => {
538 let reverse_index = (-self.n) as usize;
539 let valid_indices_len = nulls.len() - nulls.null_count();
540 if reverse_index > valid_indices_len {
541 return None;
542 }
543 nulls
544 .valid_indices()
545 .nth(valid_indices_len - reverse_index)
546 .map(|idx| idx + offset)
547 }
548 Ordering::Equal => None,
549 }
550 }
551 }
552 }
553}
554
555#[cfg(test)]
556mod tests {
557 use super::*;
558 use arrow::array::*;
559 use datafusion_common::cast::as_int32_array;
560 use datafusion_physical_expr::expressions::{Column, Literal};
561
562 fn test_i32_result(
563 expr: NthValue,
564 partition_evaluator_args: PartitionEvaluatorArgs,
565 expected: Int32Array,
566 ) -> Result<()> {
567 let arr: ArrayRef = Arc::new(Int32Array::from(vec![1, -2, 3, -4, 5, -6, 7, 8]));
568 let values = vec![arr];
569 let mut ranges: Vec<Range<usize>> = vec![];
570 for i in 0..8 {
571 ranges.push(Range {
572 start: 0,
573 end: i + 1,
574 })
575 }
576 let mut evaluator = expr.partition_evaluator(partition_evaluator_args)?;
577 let result = ranges
578 .iter()
579 .map(|range| evaluator.evaluate(&values, range))
580 .collect::<Result<Vec<ScalarValue>>>()?;
581 let result = ScalarValue::iter_to_array(result)?;
582 let result = as_int32_array(&result)?;
583 assert_eq!(expected, *result);
584 Ok(())
585 }
586
587 #[test]
588 fn first_value() -> Result<()> {
589 let expr = Arc::new(Column::new("c3", 0)) as Arc<dyn PhysicalExpr>;
590 test_i32_result(
591 NthValue::first(),
592 PartitionEvaluatorArgs::new(
593 &[expr],
594 &[Field::new("f", DataType::Int32, true).into()],
595 false,
596 false,
597 ),
598 Int32Array::from(vec![1; 8]).iter().collect::<Int32Array>(),
599 )
600 }
601
602 #[test]
603 fn last_value() -> Result<()> {
604 let expr = Arc::new(Column::new("c3", 0)) as Arc<dyn PhysicalExpr>;
605 test_i32_result(
606 NthValue::last(),
607 PartitionEvaluatorArgs::new(
608 &[expr],
609 &[Field::new("f", DataType::Int32, true).into()],
610 false,
611 false,
612 ),
613 Int32Array::from(vec![
614 Some(1),
615 Some(-2),
616 Some(3),
617 Some(-4),
618 Some(5),
619 Some(-6),
620 Some(7),
621 Some(8),
622 ]),
623 )
624 }
625
626 #[test]
627 fn nth_value_1() -> Result<()> {
628 let expr = Arc::new(Column::new("c3", 0)) as Arc<dyn PhysicalExpr>;
629 let n_value =
630 Arc::new(Literal::new(ScalarValue::Int32(Some(1)))) as Arc<dyn PhysicalExpr>;
631
632 test_i32_result(
633 NthValue::nth(),
634 PartitionEvaluatorArgs::new(
635 &[expr, n_value],
636 &[Field::new("f", DataType::Int32, true).into()],
637 false,
638 false,
639 ),
640 Int32Array::from(vec![1; 8]),
641 )?;
642 Ok(())
643 }
644
645 #[test]
646 fn nth_value_2() -> Result<()> {
647 let expr = Arc::new(Column::new("c3", 0)) as Arc<dyn PhysicalExpr>;
648 let n_value =
649 Arc::new(Literal::new(ScalarValue::Int32(Some(2)))) as Arc<dyn PhysicalExpr>;
650
651 test_i32_result(
652 NthValue::nth(),
653 PartitionEvaluatorArgs::new(
654 &[expr, n_value],
655 &[Field::new("f", DataType::Int32, true).into()],
656 false,
657 false,
658 ),
659 Int32Array::from(vec![
660 None,
661 Some(-2),
662 Some(-2),
663 Some(-2),
664 Some(-2),
665 Some(-2),
666 Some(-2),
667 Some(-2),
668 ]),
669 )?;
670 Ok(())
671 }
672
673 #[test]
674 fn nth_value_i64_min_returns_error() {
675 let expr = Arc::new(Column::new("c3", 0)) as Arc<dyn PhysicalExpr>;
676 let n_value = Arc::new(Literal::new(ScalarValue::Int64(Some(i64::MIN))))
677 as Arc<dyn PhysicalExpr>;
678
679 let err = NthValue::nth()
680 .partition_evaluator(PartitionEvaluatorArgs::new(
681 &[expr, n_value],
682 &[Field::new("f", DataType::Int32, true).into()],
683 false,
684 false,
685 ))
686 .unwrap_err();
687
688 assert!(err.to_string().starts_with(
689 "Execution error: The second argument of nth_value must not be i64::MIN"
690 ));
691 }
692}