Repository navigation
Conversation
`CREATE FUNCTION` stored its argument and return types as `DataType`s, so the SQL planner dropped the field metadata of types such as `UUID`, and a `FunctionFactory` could not tell an extension type from its storage type. The placeholders in the function body lost the metadata too. Replace `OperateFunctionArg::data_type` with `field: FieldRef` and `CreateFunction::return_type` with `return_field: Option<FieldRef>`, and keep the fields resolved by the planner. Placeholders in the body use the argument fields. Closes apache#18235.
paleolimbot
approved these changes
Nov 6, 2026
paleolimbot
left a comment
Member
There was a problem hiding this comment.
This would benefit from a second look from somebody more familiar with SQL functions than I am, but this seems reasonable to me.
The open question is how hard we want to try to actually match the input based on the input fields. My sense is that it's not worth it for this PR but I'm happy to hear other opinions.
Comment on lines
+1345
to
+1348
| field: Arc::new(Field::new("name", DataType::Utf8View, true)), | ||
| default_expr: None, | ||
| }]), | ||
| return_type: Some(DataType::Int32), | ||
| return_field: Some(Arc::new(Field::new("", DataType::Int32, true))), |
Member
There was a problem hiding this comment.
Can we use into_nullable_field_ref() here?
Comment on lines
1018
to
+1022
| type_signatures.push(TypeSignature::Exact( | ||
| args.iter().take(n).map(|a| a.data_type.clone()).collect(), | ||
| args.iter() | ||
| .take(n) | ||
| .map(|a| a.field.data_type().clone()) | ||
| .collect(), |
Member
There was a problem hiding this comment.
The Signature has no way to match an extension type here and so I think this is OK (the alternative would be to use Any and enforce in return_field()).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which issue does this PR close?
Rationale for this change
CREATE FUNCTIONdrops the metadata of argument and return types, so aFunctionFactorycannot tell an extension type from its storage type. For example, forthe factory receives
FixedSizeBinary(16)for the argument and the return type, withoutARROW:extension:name = arrow.uuid. The placeholder$1in the body also has the typeFixedSizeBinary(16).The SQL planner already resolves these types to fields with metadata (
convert_data_type_to_field), butCreateFunctionstores them asDataTypes, so the planner kept only the data type.What changes are included in this PR?
OperateFunctionArg::data_type: DataTypeis replaced byOperateFunctionArg::field: FieldRef, andCreateFunction::return_type: Option<DataType>byCreateFunction::return_field: Option<FieldRef>. This follows howPlaceholder(Enable placeholders with extension types #17986) andScalarVariable(Allow Logical expression ScalarVariable to represent an extension type or metadata #18243) moved fromDataTypetoFieldRef.CreateFunction::try_newtakes the return type asOption<FieldRef>.""if it has no name. Before, the planner built the fields for the placeholders in the body from the data types, with the same names. Now it reuses the argument fields, so the placeholders keep the metadata too.function_factoryexample and the existingFunctionFactorytests read the data type from the fields.What is the testing strategy for this PR?
create_function_keeps_extension_typesindatafusion/core/tests/user_defined/user_defined_scalar_functions.rsplansCREATE FUNCTION same_uuid(id UUID) RETURNS UUID RETURN $1with a recordingFunctionFactory. It checks that the argument field, the return field and the placeholder in the body all have the UUID extension type. The test fails if the planner drops the metadata.This is a Rust test rather than a sqllogictest because DataFusion has no built-in
FunctionFactory, so whatCREATE FUNCTIONpasses to it cannot be observed from SQL.The existing
CREATE FUNCTIONtests are updated for the new fields.Are there any user-facing changes?
Yes, this is a breaking API change, so it needs the
api changelabel:OperateFunctionArg::data_typeis renamed tofieldand is now aFieldRef;CreateFunction::return_typeis renamed toreturn_fieldand is now anOption<FieldRef>.FunctionFactoryimplementations that read these fields need to call.data_type()on them, or can use the fields to support extension types. The upgrade guide describes the migration.There is no change in SQL behavior without a custom
FunctionFactory.