You are viewing a plain text version of this content. The canonical link for it is here.
Posted to github@arrow.apache.org by "mhilton (via GitHub)" <gi...@apache.org> on 2023/07/11 10:38:07 UTC
[GitHub] [arrow-datafusion] mhilton opened a new issue, #6914: User-defined Window Functions cannot return NULL
mhilton opened a new issue, #6914:
URL: https://github.com/apache/arrow-datafusion/issues/6914
### Describe the bug
Somewhere in the execution path of user-defined window functions the results are erroneously detected be non-nullable. Any NULLs in the output then cause an `ArgumentError`.
### To Reproduce
Using this (somewhat contrived) test:
```
use datafusion::arrow::array::ArrayRef;
use datafusion::arrow::datatypes::DataType;
use datafusion::common::ScalarValue;
use datafusion::error::Result;
use datafusion::logical_expr::{
PartitionEvaluator, PartitionEvaluatorFactory, ReturnTypeFunction, Signature, Volatility,
WindowUDF,
};
use datafusion::prelude::*;
use std::ops::Range;
use std::sync::Arc;
pub fn new_window_udf() -> WindowUDF {
let return_type: ReturnTypeFunction = Arc::new(return_type);
let partition_evaluator_factory: PartitionEvaluatorFactory =
Arc::new(partition_evaluator_factory);
WindowUDF {
name: "test_window_udf".into(),
signature: Signature::exact(vec![DataType::Int64], Volatility::Immutable),
return_type,
partition_evaluator_factory,
}
}
fn return_type(_: &[DataType]) -> Result<Arc<DataType>> {
Ok(Arc::new(DataType::UInt64))
}
fn partition_evaluator_factory() -> Result<Box<dyn PartitionEvaluator>> {
Ok(Box::new(TestPartitionEvaluator {}))
}
#[derive(Debug)]
struct TestPartitionEvaluator {}
impl PartitionEvaluator for TestPartitionEvaluator {
fn evaluate(&mut self, values: &[ArrayRef], range: &Range<usize>) -> Result<ScalarValue> {
let array = Arc::clone(&values[0]);
let mut total = ScalarValue::UInt64(None);
for idx in range.clone() {
let v = ScalarValue::try_from_array(&Arc::clone(&array), idx)?;
match v {
ScalarValue::Int64(Some(n)) if n >= 0 => {
total = total.add(ScalarValue::UInt64(Some(n as u64)))?
}
_ => {}
}
}
Ok(total)
}
fn uses_window_frame(&self) -> bool {
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use datafusion::arrow::array::Int64Array;
use datafusion::arrow::datatypes::{Field, Schema};
use datafusion::arrow::record_batch::RecordBatch;
use datafusion::datasource::{DefaultTableSource, MemTable};
use datafusion::logical_expr::expr::WindowFunction;
use datafusion::logical_expr::{
LogicalPlanBuilder, WindowFrame, WindowFrameBound, WindowFrameUnits,
};
#[tokio::test]
async fn window_udf() {
let ctx = SessionContext::new();
let mut builder = Int64Array::builder(10);
for i in -5..5 {
builder.append_value(i);
}
let schema = Arc::new(Schema::new(vec![Field::new(
"numbers",
DataType::Int64,
false,
)]));
let data =
RecordBatch::try_new(Arc::clone(&schema), vec![Arc::new(builder.finish())]).unwrap();
let table = MemTable::try_new(schema, vec![vec![data]]).unwrap();
ctx.register_table("test_table", Arc::new(table)).unwrap();
let table_provider = ctx.table_provider("test_table").await.unwrap();
let plan = LogicalPlanBuilder::scan(
"test_table",
Arc::new(DefaultTableSource::new(Arc::clone(&table_provider))),
None,
)
.unwrap()
.window(vec![Expr::WindowFunction(WindowFunction::new(
datafusion::logical_expr::WindowFunction::WindowUDF(Arc::new(new_window_udf())),
vec![col("numbers")],
vec![],
vec![],
WindowFrame {
units: WindowFrameUnits::Rows,
start_bound: WindowFrameBound::Preceding(ScalarValue::UInt64(Some(1))),
end_bound: WindowFrameBound::Following(ScalarValue::UInt64(Some(1))),
},
))])
.unwrap()
.build()
.unwrap();
println!("{:?}", plan.schema());
let records = ctx
.execute_logical_plan(plan)
.await
.unwrap()
.collect()
.await
.unwrap();
assert_eq!(records.len(), 1);
}
}
```
The result is:
```
thread 'wudf::tests::window_udf' panicked at 'called `Result::unwrap()` on an `Err` value: ArrowError(InvalidArgumentError("Column 'test_window_udf(test_table.numbers)' is declared as non-nullable but contains null values"))', src/wudf.rs:118:14
```
### Expected behavior
The user-defined window function should be able to output NULLs without causing an `ArgumentError`.
### Additional context
If one requests the schema from the logical plan before execution it produces:
```
DFSchema { fields: [DFField { qualifier: Some(Bare { table: "test_table" }), field: Field { name: "numbers", data_type: Int64, nullable: false, dict_id: 0, dict_is_ordered: false, metadata: {} } }, DFField { qualifier: None, field: Field { name: "test_window_udf(test_table.numbers) ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING", data_type: UInt64, nullable: true, dict_id: 0, dict_is_ordered: false, metadata: {} } }], metadata: {} }
```
This suggests that the inconsistency creeps in somewhere in the physical-plan or execution.
--
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: github-unsubscribe@arrow.apache.org.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [arrow-datafusion] alamb commented on issue #6914: User-defined Window Functions cannot return NULL
Posted by "alamb (via GitHub)" <gi...@apache.org>.
alamb commented on issue #6914:
URL: https://github.com/apache/arrow-datafusion/issues/6914#issuecomment-1630857633
I believe @mhilton intends to fix this
--
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: github-unsubscribe@arrow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [arrow-datafusion] alamb closed issue #6914: User-defined Window Functions cannot return NULL
Posted by "alamb (via GitHub)" <gi...@apache.org>.
alamb closed issue #6914: User-defined Window Functions cannot return NULL
URL: https://github.com/apache/arrow-datafusion/issues/6914
--
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: github-unsubscribe@arrow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org