From 4dcb0ef8fce6db292e9b1aea9325f4f205206508 Mon Sep 17 00:00:00 2001 From: osipovartem Date: Fri, 18 Sep 2026 19:49:02 +0300 Subject: [PATCH] fix: preserve nullary function metadata --- datafusion/catalog/src/information_schema.rs | 54 ++++++++++++++++--- .../test_files/information_schema.slt | 18 ++++++- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/datafusion/catalog/src/information_schema.rs b/datafusion/catalog/src/information_schema.rs index d9ad7791af67c..3dc4dbaf720af 100644 --- a/datafusion/catalog/src/information_schema.rs +++ b/datafusion/catalog/src/information_schema.rs @@ -375,6 +375,7 @@ impl InformationSchemaConfig { ); } if let Some(return_type) = return_type { + // OUT-only rows still need to retain overload arity (for example, COUNT()). builder.add_parameter( catalog_name, schema_name, @@ -384,7 +385,7 @@ impl InformationSchemaConfig { None::<&str>, return_type.as_str(), None::<&str>, - false, + is_variadic, rid, ); } @@ -447,13 +448,34 @@ impl InformationSchemaConfig { } fn is_variadic(signature: &Signature) -> bool { - matches!( - signature.type_signature, - TypeSignature::Variadic(_) | TypeSignature::VariadicAny - ) + fn contains_variadic(signature: &TypeSignature) -> bool { + match signature { + TypeSignature::Variadic(_) | TypeSignature::VariadicAny => true, + TypeSignature::OneOf(signatures) => { + signatures.iter().any(contains_variadic) + } + _ => false, + } + } + + contains_variadic(&signature.type_signature) } } +fn get_nullary_return_type( + signature: &Signature, + return_field: impl FnOnce() -> Result, +) -> Option { + signature + .type_signature + .supports_zero_argument() + .then(return_field) + .and_then(Result::ok) + .map(|field| { + remove_native_type_prefix(&NativeType::from(field.data_type().clone())) + }) +} + /// get the arguments and return types of a UDF /// returns a tuple of (arg_types, return_type) fn get_udf_args_and_return_types( @@ -462,7 +484,15 @@ fn get_udf_args_and_return_types( let signature = udf.signature(); let arg_types = signature.type_signature.get_example_types(); if arg_types.is_empty() { - Ok(vec![(vec![], None)].into_iter().collect::>()) + let return_type = get_nullary_return_type(signature, || { + udf.return_field_from_args(ReturnFieldArgs { + arg_fields: &[], + scalar_arguments: &[], + }) + }); + Ok(vec![(vec![], return_type)] + .into_iter() + .collect::>()) } else { Ok(arg_types .into_iter() @@ -502,7 +532,10 @@ fn get_udaf_args_and_return_types( let signature = udaf.signature(); let arg_types = signature.type_signature.get_example_types(); if arg_types.is_empty() { - Ok(vec![(vec![], None)].into_iter().collect::>()) + let return_type = get_nullary_return_type(signature, || udaf.return_field(&[])); + Ok(vec![(vec![], return_type)] + .into_iter() + .collect::>()) } else { Ok(arg_types .into_iter() @@ -538,7 +571,12 @@ fn get_udwf_args_and_return_types( let signature = udwf.signature(); let arg_types = signature.type_signature.get_example_types(); if arg_types.is_empty() { - Ok(vec![(vec![], None)].into_iter().collect::>()) + let return_type = get_nullary_return_type(signature, || { + udwf.field(WindowUDFFieldArgs::new(&[], udwf.name())) + }); + Ok(vec![(vec![], return_type)] + .into_iter() + .collect::>()) } else { Ok(arg_types .into_iter() diff --git a/datafusion/sqllogictest/test_files/information_schema.slt b/datafusion/sqllogictest/test_files/information_schema.slt index 18f9e5c62d435..83315f57cbffc 100644 --- a/datafusion/sqllogictest/test_files/information_schema.slt +++ b/datafusion/sqllogictest/test_files/information_schema.slt @@ -860,7 +860,7 @@ datafusion public date_trunc datafusion public date_trunc FUNCTION true String S datafusion public date_trunc datafusion public date_trunc FUNCTION true Time(ns) SCALAR Truncates a timestamp or time value to a specified precision. date_trunc(precision, expression) datafusion public date_trunc datafusion public date_trunc FUNCTION true Timestamp(ns) SCALAR Truncates a timestamp or time value to a specified precision. date_trunc(precision, expression) datafusion public date_trunc datafusion public date_trunc FUNCTION true Timestamp(ns, "+TZ") SCALAR Truncates a timestamp or time value to a specified precision. date_trunc(precision, expression) -datafusion public rank datafusion public rank FUNCTION true NULL WINDOW Returns the rank of the current row within its partition, allowing gaps between ranks. This function provides a ranking similar to `row_number`, but skips ranks for identical values. rank() +datafusion public rank datafusion public rank FUNCTION true UInt64 WINDOW Returns the rank of the current row within its partition, allowing gaps between ranks. This function provides a ranking similar to `row_number`, but skips ranks for identical values. rank() datafusion public string_agg datafusion public string_agg FUNCTION true String AGGREGATE Concatenates the values of string expressions and places separator values between them. If ordering is required, strings are concatenated in the specified order. This aggregation function can only mix DISTINCT and ORDER BY if the ordering expression is exactly the same as the first argument expression. string_agg([DISTINCT] expression, delimiter [ORDER BY expression]) query B @@ -887,6 +887,7 @@ datafusion public date_trunc 1 OUT NULL Timestamp(ns) NULL false 3 datafusion public date_trunc 1 IN precision String NULL false 4 datafusion public date_trunc 2 IN expression Timestamp(ns, "+TZ") NULL false 4 datafusion public date_trunc 1 OUT NULL Timestamp(ns, "+TZ") NULL false 4 +datafusion public rank 1 OUT NULL UInt64 NULL false 0 datafusion public string_agg 2 IN delimiter Null NULL false 0 datafusion public string_agg 1 IN expression String NULL false 0 datafusion public string_agg 1 OUT NULL String NULL false 0 @@ -902,6 +903,21 @@ repeat Int64 2 IN 0 repeat String 1 IN 0 repeat String 1 OUT 0 +# Nullary functions retain their OUT metadata, and OneOf signatures retain a +# nested variadic marker even when no representative input type is available. +query TTTB rowsort +select specific_name, data_type, parameter_mode, is_variadic +from information_schema.parameters +where specific_name in ('count', 'current_date'); +---- +count Int64 OUT true +current_date Date OUT false + +query TT??TTT +show functions like 'count'; +---- +count Int64 [NULL] [NULL] AGGREGATE Returns the number of non-null values in the specified column. To include null values in the total count, use `count(*)`. count(expression) + query TT??TTT rowsort show functions like 'date_trunc'; ----