apache / datafusion

Apache DataFusion SQL Query Engine
https://datafusion.apache.org/
Apache License 2.0
5.91k stars 1.12k forks source link

Support creating arrays with non-nullable elements in `make_array` #11923

Open Kimahriman opened 1 month ago

Kimahriman commented 1 month ago

Is your feature request related to a problem or challenge?

make_array currently hard codes that all return types and arrays returned from invoke have a nullable element.

Describe the solution you'd like

Check the input expressions, and if all are non-nullable, the array element should be non-nullable

Describe alternatives you've considered

No response

Additional context

Discovered this while trying to implement array creation support in datafusion-comet

jayzhan211 commented 1 month ago

What might be the downside if the array is always nullable? Loss the possibility for non-null optimization? If there is such a need for most of the functions, we might not need return_type anymore but return_type_from_exprs instead. That is a huge change.

Kimahriman commented 1 month ago

What might be the downside if the array is always nullable? Loss the possibility for non-null optimization

That would be the main thing. In this case I came across this trying to integrate array support into datafusion-comet and it was inconsistent with Spark's handling of arrays. Maybe not the best reason to change, but it also seems weird to have nullability be tracked but not track it correctly.

If there is such a need for most of the functions, we might not need return_type anymore but return_type_from_exprs instead. That is a huge change.

The actual change per function isn't that much, working on what the make_array update would look like.

Kimahriman commented 1 month ago

One of the downstream affects would be ever so slightly increased encoding/decoding of Parquet files with arrays of non-nullable elements

jayzhan211 commented 1 month ago

@alamb, I think we could have a breaking change on return_type with ReturnTypeArgs, and remove return_type_from_exprs.

fn return_type(&self, _args: ReturnTypeArgs) -> Result<DataType> {

struct ReturnTypeArgs<'a> {
    args: &'a [Expr],
    schema: &'a dyn ExprSchema,
    arg_types: &'a [DataType],
}

Alternatively, we switch all the function from return_type to return_type_from_exprs, since I think most of them would expect the same nullable from input.

Kimahriman commented 1 month ago

After digging a little more it seems like there's some other issues that might make this significantly more difficult:

jayzhan211 commented 1 month ago

We could have another invoke_with_schema function to get the nullable from schema. Of course, another incompatible change 😢

We have schema here https://github.com/apache/datafusion/blob/18193e6224603c92ce1ab16136ffcd926ca267b5/datafusion/physical-expr/src/scalar_function.rs#L214-L239

jayzhan211 commented 1 month ago

I remember there was a discussion about replacing ColumnarValue::Array(ArrayRef) with Recordbatch, which contains Schema

#[derive(Clone, Debug, PartialEq)]
pub struct RecordBatch {
    schema: SchemaRef,
    columns: Vec<Arc<dyn Array>>,

    /// The number of rows in this RecordBatch
    ///
    /// This is stored separately from the columns to handle the case of no columns
    row_count: usize,
}

Maybe Recordbatch is what we need for invoke 🤔