@@ -3451,3 +3451,93 @@ def test_round(self):
34513451 "duckdb" : "SELECT ROUND_EVEN(CAST('2.25' AS DECIMAL), 1) AS value" ,
34523452 },
34533453 )
3454+
3455+ def test_approx_quantiles (self ):
3456+ self .validate_identity ("APPROX_QUANTILES(x, 2)" )
3457+ self .validate_identity ("APPROX_QUANTILES(FALSE OR TRUE, 2)" )
3458+ self .validate_identity ("APPROX_QUANTILES((SELECT 1 AS val), CAST(2.1 AS INT64))" )
3459+ self .validate_identity ("APPROX_QUANTILES(DISTINCT x, 2)" )
3460+ self .validate_identity ("APPROX_QUANTILES(x, 2 RESPECT NULLS)" )
3461+ self .validate_identity ("APPROX_QUANTILES(x, 2 IGNORE NULLS)" )
3462+ self .validate_identity ("APPROX_QUANTILES(DISTINCT x, 2 RESPECT NULLS)" )
3463+
3464+ def test_approx_quantiles_to_duckdb (self ):
3465+ self .validate_all (
3466+ "APPROX_QUANTILES(x, 1)" ,
3467+ write = {"duckdb" : "APPROX_QUANTILE(x, [0, 1])" },
3468+ )
3469+ self .validate_all (
3470+ "APPROX_QUANTILES(x, 2)" ,
3471+ write = {"duckdb" : "APPROX_QUANTILE(x, [0, 0.5, 1])" },
3472+ )
3473+ self .validate_all (
3474+ "APPROX_QUANTILES(x, 4)" ,
3475+ write = {"duckdb" : "APPROX_QUANTILE(x, [0, 0.25, 0.5, 0.75, 1])" },
3476+ )
3477+ self .validate_all (
3478+ "APPROX_QUANTILES(DISTINCT x, 2)" ,
3479+ write = {"duckdb" : "APPROX_QUANTILE(DISTINCT x, [0, 0.5, 1])" },
3480+ )
3481+
3482+ with self .subTest ("APPROX_QUANTILES 100 buckets" ):
3483+ result = self .parse_one ("APPROX_QUANTILES(x, 100)" ).sql ("duckdb" )
3484+ self .assertEqual (result .count ("APPROX_QUANTILE(" ), 1 )
3485+ self .assertIn ("0.01" , result )
3486+ self .assertIn ("0.99" , result )
3487+ self .assertRegex (result , r"APPROX_QUANTILE\(x, \[.*\]\)" )
3488+
3489+ for expr in ("x + y" , "CASE WHEN x > 0 THEN x ELSE 0 END" , "ABS(x)" ):
3490+ with self .subTest (expr = expr ):
3491+ self .validate_all (
3492+ f"APPROX_QUANTILES({ expr } , 2)" ,
3493+ write = {"duckdb" : f"APPROX_QUANTILE({ expr } , [0, 0.5, 1])" },
3494+ )
3495+
3496+ with self .subTest ("non-literal bucket count" ):
3497+ with self .assertRaises (UnsupportedError ):
3498+ self .parse_one ("APPROX_QUANTILES(x, bucket_count)" ).sql (
3499+ "duckdb" , unsupported_level = ErrorLevel .RAISE
3500+ )
3501+
3502+ with self .subTest ("non-integer bucket count" ):
3503+ for value in ("0" , "-1" , "2.5" ):
3504+ with self .subTest (value = value ):
3505+ with self .assertRaises (UnsupportedError ):
3506+ self .parse_one (f"APPROX_QUANTILES(x, { value } )" ).sql (
3507+ "duckdb" , unsupported_level = ErrorLevel .RAISE
3508+ )
3509+
3510+ with self .subTest ("NULL bucket count" ):
3511+ with self .assertRaises (UnsupportedError ):
3512+ self .parse_one ("APPROX_QUANTILES(x, NULL)" ).sql (
3513+ "duckdb" , unsupported_level = ErrorLevel .RAISE
3514+ )
3515+
3516+ with self .subTest ("missing bucket count" ):
3517+ with self .assertRaises (UnsupportedError ):
3518+ self .parse_one ("APPROX_QUANTILES(x)" ).sql (
3519+ "duckdb" , unsupported_level = ErrorLevel .RAISE
3520+ )
3521+
3522+ with self .subTest ("missing bucket count with DISTINCT" ):
3523+ with self .assertRaises (UnsupportedError ):
3524+ self .parse_one ("APPROX_QUANTILES(DISTINCT x)" ).sql (
3525+ "duckdb" , unsupported_level = ErrorLevel .RAISE
3526+ )
3527+
3528+ with self .subTest ("APPROX_QUANTILES IGNORE NULLS" ):
3529+ # No warning: IGNORE NULLS is the default behavior in DuckDB
3530+ from sqlglot .generator import logger as generator_logger
3531+
3532+ with mock .patch .object (generator_logger , "warning" ) as mock_warning :
3533+ self .validate_all (
3534+ "APPROX_QUANTILES(x, 2 IGNORE NULLS)" ,
3535+ write = {"duckdb" : "APPROX_QUANTILE(x, [0, 0.5, 1])" },
3536+ )
3537+ mock_warning .assert_not_called ()
3538+
3539+ with self .subTest ("APPROX_QUANTILES RESPECT NULLS" ):
3540+ with self .assertRaises (UnsupportedError ):
3541+ self .parse_one ("APPROX_QUANTILES(x, 2 RESPECT NULLS)" ).sql (
3542+ "duckdb" , unsupported_level = ErrorLevel .RAISE
3543+ )
0 commit comments