@@ -347,18 +347,18 @@ class FunctionInfo(CatalogSchemaObject):
347347 # schema.serialize().to_pybytes()
348348 output_schema : SerializedSchema
349349
350- # Behavior fields
351- stability : FunctionStability = FunctionStability . CONSISTENT
352- null_handling : NullHandling = NullHandling . DEFAULT
350+ # Scalar function behavior fields (None for non-scalar functions)
351+ stability : FunctionStability | None = None
352+ null_handling : NullHandling | None = None
353353
354354 # Documentation fields
355355 examples : list [str ] = field (default_factory = list )
356356 categories : list [str ] = field (default_factory = list )
357357
358- # Table function capabilities
359- projection_pushdown : bool = True
360- filter_pushdown : bool = False
361- order_preservation : OrderPreservation = OrderPreservation . PRESERVES_ORDER
358+ # Table function capabilities (None for scalar functions)
359+ projection_pushdown : bool | None = None
360+ filter_pushdown : bool | None = None
361+ order_preservation : OrderPreservation | None = None
362362 max_workers : int | None = None
363363
364364 # Aggregate function fields (future)
@@ -377,16 +377,16 @@ class FunctionInfo(CatalogSchemaObject):
377377 pa .field ("output_schema" , pa .binary (), nullable = False ),
378378 pa .field ("comment" , pa .string (), nullable = True ),
379379 pa .field ("tags" , pa .map_ (pa .string (), pa .string ()), nullable = False ),
380- # Behavior fields (enum values serialized as strings )
381- pa .field ("stability" , pa .string (), nullable = False ),
382- pa .field ("null_handling" , pa .string (), nullable = False ),
380+ # Scalar function behavior fields (nullable for non-scalar functions )
381+ pa .field ("stability" , pa .string (), nullable = True ),
382+ pa .field ("null_handling" , pa .string (), nullable = True ),
383383 # Documentation fields
384384 pa .field ("examples" , pa .list_ (pa .string ()), nullable = False ),
385385 pa .field ("categories" , pa .list_ (pa .string ()), nullable = False ),
386- # Table function capabilities
387- pa .field ("projection_pushdown" , pa .bool_ (), nullable = False ),
388- pa .field ("filter_pushdown" , pa .bool_ (), nullable = False ),
389- pa .field ("order_preservation" , pa .string (), nullable = False ),
386+ # Table function capabilities (nullable for scalar functions)
387+ pa .field ("projection_pushdown" , pa .bool_ (), nullable = True ),
388+ pa .field ("filter_pushdown" , pa .bool_ (), nullable = True ),
389+ pa .field ("order_preservation" , pa .string (), nullable = True ),
390390 pa .field ("max_workers" , pa .int32 (), nullable = True ),
391391 # Aggregate function fields
392392 pa .field ("order_dependent" , pa .string (), nullable = False ),
@@ -408,16 +408,22 @@ def serialize(self) -> bytes:
408408 "output_schema" : self .output_schema ,
409409 "comment" : self .comment ,
410410 "tags" : self .tags ,
411- # Behavior fields (enums serialized as name strings)
412- "stability" : self .stability .name ,
413- "null_handling" : self .null_handling .name ,
411+ # Scalar function behavior fields (None for non-scalar)
412+ "stability" : self .stability .name if self .stability else None ,
413+ "null_handling" : (
414+ self .null_handling .name if self .null_handling else None
415+ ),
414416 # Documentation fields
415417 "examples" : self .examples ,
416418 "categories" : self .categories ,
417- # Table function capabilities
419+ # Table function capabilities (None for scalar)
418420 "projection_pushdown" : self .projection_pushdown ,
419421 "filter_pushdown" : self .filter_pushdown ,
420- "order_preservation" : self .order_preservation .name ,
422+ "order_preservation" : (
423+ self .order_preservation .name
424+ if self .order_preservation
425+ else None
426+ ),
421427 "max_workers" : self .max_workers ,
422428 # Aggregate function fields
423429 "order_dependent" : self .order_dependent .name ,
@@ -457,18 +463,28 @@ def deserialize(cls, batch: pa.RecordBatch) -> Self:
457463 output_schema = SerializedSchema (row ["output_schema" ]),
458464 comment = row .get ("comment" ),
459465 tags = dict (row ["tags" ]) if row ["tags" ] else {},
460- # Behavior fields (with backward-compatible defaults)
461- stability = FunctionStability [row .get ("stability" , "CONSISTENT" )],
462- null_handling = NullHandling [row .get ("null_handling" , "DEFAULT" )],
466+ # Scalar function behavior fields (None for non-scalar functions)
467+ stability = (
468+ FunctionStability [row ["stability" ]]
469+ if row .get ("stability" ) is not None
470+ else None
471+ ),
472+ null_handling = (
473+ NullHandling [row ["null_handling" ]]
474+ if row .get ("null_handling" ) is not None
475+ else None
476+ ),
463477 # Documentation fields
464478 examples = list (row .get ("examples" ) or []),
465479 categories = list (row .get ("categories" ) or []),
466- # Table function capabilities
467- projection_pushdown = row .get ("projection_pushdown" , True ),
468- filter_pushdown = row .get ("filter_pushdown" , False ),
469- order_preservation = OrderPreservation [
470- row .get ("order_preservation" , "PRESERVES_ORDER" )
471- ],
480+ # Table function capabilities (None for scalar functions)
481+ projection_pushdown = row .get ("projection_pushdown" ),
482+ filter_pushdown = row .get ("filter_pushdown" ),
483+ order_preservation = (
484+ OrderPreservation [row ["order_preservation" ]]
485+ if row .get ("order_preservation" ) is not None
486+ else None
487+ ),
472488 max_workers = row .get ("max_workers" ),
473489 # Aggregate function fields
474490 order_dependent = OrderDependence [
@@ -1149,6 +1165,8 @@ def _function_to_info(self, func_cls: type, schema_name: str) -> FunctionInfo:
11491165 output_schema = func_cls .catalog_output_schema () # type: ignore[attr-defined]
11501166 output_bytes = SerializedSchema (output_schema .serialize ().to_pybytes ())
11511167
1168+ is_scalar = func_type == FunctionType .SCALAR
1169+
11521170 return FunctionInfo (
11531171 name = meta .name ,
11541172 schema_name = schema_name ,
@@ -1157,17 +1175,17 @@ def _function_to_info(self, func_cls: type, schema_name: str) -> FunctionInfo:
11571175 output_schema = output_bytes ,
11581176 comment = meta .description or None ,
11591177 tags = {},
1160- # Behavior fields
1161- stability = meta .stability ,
1162- null_handling = meta .null_handling ,
1178+ # Scalar function behavior fields (None for non-scalar)
1179+ stability = meta .stability if is_scalar else None ,
1180+ null_handling = meta .null_handling if is_scalar else None ,
11631181 # Documentation fields
11641182 examples = [ex .sql for ex in meta .examples ],
11651183 categories = meta .categories ,
1166- # Table function capabilities
1167- projection_pushdown = meta .projection_pushdown ,
1168- filter_pushdown = meta .filter_pushdown ,
1169- order_preservation = meta .preserves_order ,
1170- max_workers = meta .max_workers ,
1184+ # Table function capabilities (None for scalar)
1185+ projection_pushdown = None if is_scalar else meta .projection_pushdown ,
1186+ filter_pushdown = None if is_scalar else meta .filter_pushdown ,
1187+ order_preservation = None if is_scalar else meta .preserves_order ,
1188+ max_workers = None if is_scalar else meta .max_workers ,
11711189 # Aggregate function fields
11721190 order_dependent = meta .order_dependent ,
11731191 distinct_dependent = meta .distinct_dependent ,
0 commit comments