1313# limitations under the License.
1414from __future__ import annotations
1515
16+ import dataclasses
1617import functools
1718
1819import sqlglot .expressions as sge
1920
21+ from bigframes import dtypes
2022from bigframes .core import expression
2123import bigframes .core .compile .sqlglot .sqlglot_ir as ir
2224import bigframes .operations as ops
2325
2426
27+ @dataclasses .dataclass (frozen = True )
28+ class TypedExpr :
29+ """SQLGlot expression with type."""
30+
31+ expr : sge .Expression
32+ dtype : dtypes .ExpressionType
33+
34+
2535@functools .singledispatch
2636def compile_scalar_expression (
2737 expression : expression .Expression ,
@@ -50,9 +60,12 @@ def compile_constant_expression(
5060
5161
5262@compile_scalar_expression .register
53- def compile_op_expression (expr : expression .OpExpression ):
63+ def compile_op_expression (expr : expression .OpExpression ) -> sge . Expression :
5464 # Non-recursively compiles the children scalar expressions.
55- args = tuple (map (compile_scalar_expression , expr .inputs ))
65+ args = tuple (
66+ TypedExpr (compile_scalar_expression (input ), input .output_type )
67+ for input in expr .inputs
68+ )
5669
5770 op = expr .op
5871 op_name = expr .op .__class__ .__name__
@@ -79,8 +92,10 @@ def compile_op_expression(expr: expression.OpExpression):
7992
8093
8194# TODO: add parenthesize for operators
82- def compile_addop (
83- op : ops .AddOp , left : sge .Expression , right : sge .Expression
84- ) -> sge .Expression :
85- # TODO: support addop for string dtype.
86- return sge .Add (this = left , expression = right )
95+ def compile_addop (op : ops .AddOp , left : TypedExpr , right : TypedExpr ) -> sge .Expression :
96+ if left .dtype == dtypes .STRING_DTYPE and right .dtype == dtypes .STRING_DTYPE :
97+ # String addition
98+ return sge .Concat (expressions = [left .expr , right .expr ])
99+
100+ # Numerical addition
101+ return sge .Add (this = left .expr , expression = right .expr )
0 commit comments