@@ -290,6 +290,37 @@ def _anyvalue_sql(self: DuckDB.Generator, expression: exp.AnyValue) -> str:
290290 return self .function_fallback_sql (expression )
291291
292292
293+ def _greatest_sql (self : DuckDB .Generator , expression : exp .Greatest ) -> str :
294+ """
295+ Handle GREATEST function to match BigQuery's NULL and NaN behavior.
296+ In BigQuery:
297+ - if any argument is NULL, the result is NULL
298+ In DuckDB:
299+ - NULLs are ignored, returns greatest non-NULL value.
300+
301+ If any argument is NaN (and other arugments are not NULL), DuckDB returns NaN, which matches BigQuery's behavior
302+ """
303+ all_args = [expression .this ] + expression .expressions
304+
305+ greatest_expr = self .func ("GREATEST" , * all_args )
306+
307+ # Check for NULL: arg1 IS NULL OR arg2 IS NULL OR ...
308+ null_checks = []
309+ for arg in all_args :
310+ null_checks .append (exp .Is (this = arg , expression = exp .Null ()))
311+
312+ is_any_null_expr : exp .Expression = null_checks [0 ]
313+ for null_check in null_checks [1 :]:
314+ is_any_null_expr = exp .Or (this = is_any_null_expr , expression = null_check )
315+
316+ # Create CASE expression - only need NULL check, DuckDB handles NaN natively
317+ case_expr = exp .Case (
318+ ifs = [exp .If (this = is_any_null_expr , true = exp .Null ())], default = greatest_expr
319+ )
320+
321+ return self .sql (case_expr )
322+
323+
293324class DuckDB (Dialect ):
294325 NULL_ORDERING = "nulls_are_last"
295326 SUPPORTS_USER_DEFINED_TYPES = True
@@ -751,6 +782,7 @@ class Generator(generator.Generator):
751782 exp .EuclideanDistance : rename_func ("LIST_DISTANCE" ),
752783 exp .GenerateDateArray : _generate_datetime_array_sql ,
753784 exp .GenerateTimestampArray : _generate_datetime_array_sql ,
785+ exp .Greatest : _greatest_sql ,
754786 exp .GroupConcat : lambda self , e : groupconcat_sql (self , e , within_group = False ),
755787 exp .Explode : rename_func ("UNNEST" ),
756788 exp .IntDiv : lambda self , e : self .binary (e , "//" ),
0 commit comments