@@ -38,6 +38,37 @@ def compile(
3838 return UNARY_OP_REGISTRATION [op ](op , column , window = window )
3939
4040
41+ @UNARY_OP_REGISTRATION .register (agg_ops .ApproxQuartilesOp )
42+ def _ (
43+ op : agg_ops .ApproxQuartilesOp ,
44+ column : typed_expr .TypedExpr ,
45+ window : typing .Optional [window_spec .WindowSpec ] = None ,
46+ ) -> sge .Expression :
47+ if window is not None :
48+ raise NotImplementedError ("Approx Quartiles with windowing is not supported." )
49+ # APPROX_QUANTILES returns an array of the quartiles, so we need to index it.
50+ # The op.quartile is 1-based for the quartile, but array is 0-indexed.
51+ # The quartiles are Q0, Q1, Q2, Q3, Q4. op.quartile is 1, 2, or 3.
52+ # The array has 5 elements (for N=4 intervals).
53+ # So we want the element at index `op.quartile`.
54+ approx_quantiles_expr = sge .func ("APPROX_QUANTILES" , column .expr , sge .convert (4 ))
55+ return sge .Bracket (
56+ this = approx_quantiles_expr ,
57+ expressions = [sge .func ("OFFSET" , sge .convert (op .quartile ))],
58+ )
59+
60+
61+ @UNARY_OP_REGISTRATION .register (agg_ops .ApproxTopCountOp )
62+ def _ (
63+ op : agg_ops .ApproxTopCountOp ,
64+ column : typed_expr .TypedExpr ,
65+ window : typing .Optional [window_spec .WindowSpec ] = None ,
66+ ) -> sge .Expression :
67+ if window is not None :
68+ raise NotImplementedError ("Approx top count with windowing is not supported." )
69+ return sge .func ("APPROX_TOP_COUNT" , column .expr , sge .convert (op .number ))
70+
71+
4172@UNARY_OP_REGISTRATION .register (agg_ops .CountOp )
4273def _ (
4374 op : agg_ops .CountOp ,
@@ -53,10 +84,7 @@ def _(
5384 column : typed_expr .TypedExpr ,
5485 window : typing .Optional [window_spec .WindowSpec ] = None ,
5586) -> sge .Expression :
56- # Ranking functions do not support window framing clauses.
57- return apply_window_if_present (
58- sge .func ("DENSE_RANK" ), window , include_framing_clauses = False
59- )
87+ return apply_window_if_present (sge .func ("DENSE_RANK" ), window )
6088
6189
6290@UNARY_OP_REGISTRATION .register (agg_ops .MaxOp )
@@ -109,13 +137,23 @@ def _(
109137 return apply_window_if_present (sge .func ("MIN" , column .expr ), window )
110138
111139
112- @UNARY_OP_REGISTRATION .register (agg_ops .SizeUnaryOp )
140+ @UNARY_OP_REGISTRATION .register (agg_ops .QuantileOp )
113141def _ (
114- op : agg_ops .SizeUnaryOp ,
115- _ ,
142+ op : agg_ops .QuantileOp ,
143+ column : typed_expr . TypedExpr ,
116144 window : typing .Optional [window_spec .WindowSpec ] = None ,
117145) -> sge .Expression :
118- return apply_window_if_present (sge .func ("COUNT" , sge .convert (1 )), window )
146+ # TODO: Support interpolation argument
147+ # TODO: Support percentile_disc
148+ result : sge .Expression = sge .func ("PERCENTILE_CONT" , column .expr , sge .convert (op .q ))
149+ if window is None :
150+ # PERCENTILE_CONT is a navigation function, not an aggregate function, so it always needs an OVER clause.
151+ result = sge .Window (this = result )
152+ else :
153+ result = apply_window_if_present (result , window )
154+ if op .should_floor_result :
155+ result = sge .Cast (this = sge .func ("FLOOR" , result ), to = "INT64" )
156+ return result
119157
120158
121159@UNARY_OP_REGISTRATION .register (agg_ops .RankOp )
@@ -124,10 +162,16 @@ def _(
124162 column : typed_expr .TypedExpr ,
125163 window : typing .Optional [window_spec .WindowSpec ] = None ,
126164) -> sge .Expression :
127- # Ranking functions do not support window framing clauses.
128- return apply_window_if_present (
129- sge .func ("RANK" ), window , include_framing_clauses = False
130- )
165+ return apply_window_if_present (sge .func ("RANK" ), window )
166+
167+
168+ @UNARY_OP_REGISTRATION .register (agg_ops .SizeUnaryOp )
169+ def _ (
170+ op : agg_ops .SizeUnaryOp ,
171+ _ ,
172+ window : typing .Optional [window_spec .WindowSpec ] = None ,
173+ ) -> sge .Expression :
174+ return apply_window_if_present (sge .func ("COUNT" , sge .convert (1 )), window )
131175
132176
133177@UNARY_OP_REGISTRATION .register (agg_ops .SumOp )
0 commit comments