1616
1717import typing
1818
19- import sqlglot
19+ import sqlglot as sg
2020import sqlglot .expressions as sge
2121
2222from bigframes import operations as ops
2323from bigframes .core .compile .sqlglot .expressions .typed_expr import TypedExpr
2424import bigframes .core .compile .sqlglot .scalar_compiler as scalar_compiler
25+ import bigframes .dtypes as dtypes
2526
2627register_unary_op = scalar_compiler .scalar_op_compiler .register_unary_op
27-
28-
29- @register_unary_op (ops .ArrayToStringOp , pass_op = True )
30- def _ (expr : TypedExpr , op : ops .ArrayToStringOp ) -> sge .Expression :
31- return sge .ArrayToString (this = expr .expr , expression = f"'{ op .delimiter } '" )
28+ register_nary_op = scalar_compiler .scalar_op_compiler .register_nary_op
3229
3330
3431@register_unary_op (ops .ArrayIndexOp , pass_op = True )
@@ -41,17 +38,45 @@ def _(expr: TypedExpr, op: ops.ArrayIndexOp) -> sge.Expression:
4138 )
4239
4340
41+ @register_unary_op (ops .ArrayReduceOp , pass_op = True )
42+ def _ (expr : TypedExpr , op : ops .ArrayReduceOp ) -> sge .Expression :
43+ sub_expr = sg .to_identifier ("bf_arr_reduce_uid" )
44+ sub_type = dtypes .get_array_inner_type (expr .dtype )
45+
46+ if op .aggregation .order_independent :
47+ from bigframes .core .compile .sqlglot .aggregations import unary_compiler
48+
49+ agg_expr = unary_compiler .compile (op .aggregation , TypedExpr (sub_expr , sub_type ))
50+ else :
51+ from bigframes .core .compile .sqlglot .aggregations import ordered_unary_compiler
52+
53+ agg_expr = ordered_unary_compiler .compile (
54+ op .aggregation , TypedExpr (sub_expr , sub_type )
55+ )
56+
57+ return (
58+ sge .select (agg_expr )
59+ .from_ (
60+ sge .Unnest (
61+ expressions = [expr .expr ],
62+ alias = sge .TableAlias (columns = [sub_expr ]),
63+ )
64+ )
65+ .subquery ()
66+ )
67+
68+
4469@register_unary_op (ops .ArraySliceOp , pass_op = True )
4570def _ (expr : TypedExpr , op : ops .ArraySliceOp ) -> sge .Expression :
46- slice_idx = sqlglot .to_identifier ("slice_idx" )
71+ slice_idx = sg .to_identifier ("slice_idx" )
4772
4873 conditions : typing .List [sge .Predicate ] = [slice_idx >= op .start ]
4974
5075 if op .stop is not None :
5176 conditions .append (slice_idx < op .stop )
5277
5378 # local name for each element in the array
54- el = sqlglot .to_identifier ("el" )
79+ el = sg .to_identifier ("el" )
5580
5681 selected_elements = (
5782 sge .select (el )
@@ -66,3 +91,27 @@ def _(expr: TypedExpr, op: ops.ArraySliceOp) -> sge.Expression:
6691 )
6792
6893 return sge .array (selected_elements )
94+
95+
96+ @register_unary_op (ops .ArrayToStringOp , pass_op = True )
97+ def _ (expr : TypedExpr , op : ops .ArrayToStringOp ) -> sge .Expression :
98+ return sge .ArrayToString (this = expr .expr , expression = f"'{ op .delimiter } '" )
99+
100+
101+ @register_nary_op (ops .ToArrayOp )
102+ def _ (* exprs : TypedExpr ) -> sge .Expression :
103+ do_upcast_bool = any (
104+ dtypes .is_numeric (expr .dtype , include_bool = False ) for expr in exprs
105+ )
106+ if do_upcast_bool :
107+ sg_exprs = [_coerce_bool_to_int (expr ) for expr in exprs ]
108+ else :
109+ sg_exprs = [expr .expr for expr in exprs ]
110+ return sge .Array (expressions = sg_exprs )
111+
112+
113+ def _coerce_bool_to_int (typed_expr : TypedExpr ) -> sge .Expression :
114+ """Coerce boolean expression to integer."""
115+ if typed_expr .dtype == dtypes .BOOL_DTYPE :
116+ return sge .Cast (this = typed_expr .expr , to = "INT64" )
117+ return typed_expr .expr
0 commit comments