Jefffrey commented on code in PR #22134:
URL: https://github.com/apache/datafusion/pull/22134#discussion_r3622544476
##########
datafusion/sqllogictest/test_files/window.slt:
##########
@@ -4178,6 +4178,286 @@ select lag(a, 1, null) over (order by a) from (select 1
a union all select 2 a)
NULL
1
+# test LAG with another column as default (first row falls back to b)
+query II
+select a, lag(a, 1, b) over (order by a)
+from (select 1 a, 10 b union all select 2 a, 20 b union all select 3 a, 30 b)
Review Comment:
we should probably create a common table to be used across these tests, as
the union all way of creating rows is verbose
##########
datafusion/functions-window/src/lead_lag.rs:
##########
@@ -649,17 +771,44 @@ impl PartitionEvaluator for WindowShiftEvaluator {
values: &[ArrayRef],
_num_rows: usize,
) -> Result<ArrayRef> {
- // LEAD, LAG window functions take single column, values will have
size 1
+ // LEAD, LAG window functions take single column, values will have
size:
+ // '1' - when default_value is a ScalarValue (or we simply did not
specify it)
+ // '2' - when default_value is a PhysicalExpr
let value = &values[0];
- if !self.ignore_nulls {
- shift_with_default_value(value, self.shift_offset,
&self.default_value)
- } else {
- evaluate_all_with_ignore_null(
- value,
- self.shift_offset,
- &self.default_value,
- self.is_lag(),
- )
+ match &self.default_value {
+ DefaultValue::Literal(scalar) => {
+ if !self.ignore_nulls {
+ shift_with_default_value(value, self.shift_offset,
&scalar.clone())
+ } else {
+ evaluate_all_with_ignore_null(
+ value,
+ self.shift_offset,
+ &scalar.clone(),
+ self.is_lag(),
+ )
+ }
+ }
+ DefaultValue::Expression => {
+ let default_array = values.get(1).cloned().unwrap_or_else(|| {
+ Arc::new(arrow::array::NullArray::new(value.len()))
+ });
+ let default_array = if default_array.data_type() !=
value.data_type() {
+ arrow::compute::kernels::cast::cast(&default_array,
value.data_type())
+ .map_err(|e| arrow_datafusion_err!(e))?
+ } else {
+ default_array
+ };
Review Comment:
i do wonder if theres a way to pull this casting logic away from execution
and into planning, perhaps by changing to a user defined signature for the
window function and ensuring the default argument is coerced to the same type
as the primary expression
##########
datafusion/functions-window/src/utils.rs:
##########
@@ -51,6 +54,31 @@ pub(crate) fn get_scalar_value_from_args(
})
}
+pub(crate) fn get_default_value_from_args(
Review Comment:
this should probably be located in `lead_lag.rs` since it relies on an enum
defined there too
##########
datafusion/functions-window/src/lead_lag.rs:
##########
@@ -501,6 +518,89 @@ fn shift_with_default_value(
}
}
+fn shift_with_array_default(
+ array: &ArrayRef,
+ offset: i64,
+ default_values: &ArrayRef,
+) -> Result<ArrayRef> {
+ use datafusion_common::arrow::compute::concat;
+
+ let value_len = array.len() as i64;
+ if offset == 0 {
+ return Ok(Arc::clone(array));
+ }
+ if offset == i64::MIN || offset.abs() >= value_len {
+ return Ok(Arc::clone(default_values));
+ }
+
+ let slice_offset = (-offset).clamp(0, value_len) as usize;
+ let length = array.len() - offset.unsigned_abs() as usize;
+ let slice = array.slice(slice_offset, length);
+
+ let defaults_slice = if offset > 0 {
+ // Lag: defaults go at the beginning
+ default_values.slice(0, offset.unsigned_abs() as usize)
+ } else {
+ // Lead: defaults go at the end
+ let start = default_values.len() - offset.unsigned_abs() as usize;
+ default_values.slice(start, offset.unsigned_abs() as usize)
+ };
+
+ if offset > 0 {
+ concat(&[defaults_slice.as_ref(), slice.as_ref()])
+ } else {
+ concat(&[slice.as_ref(), defaults_slice.as_ref()])
+ }
+ .map_err(|e| arrow_datafusion_err!(e))
+}
+
+fn evaluate_all_with_ignore_null_and_array_default(
+ array: &ArrayRef,
+ offset: i64,
+ default_values: &ArrayRef,
+ is_lag: bool,
+) -> Result<ArrayRef> {
+ let valid_indices: Vec<usize> =
array.nulls().unwrap().valid_indices().collect();
Review Comment:
this unwrap on nulls might not be safe, see recent PR
- https://github.com/apache/datafusion/pull/23706
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]