@@ -323,9 +323,9 @@ def _convert_expr(e: exp.Expression) -> Any:
323323 # NULL propagation. infer_type/emit already handle "sub".
324324 return BinaryOp ("sub" , Literal (0 ), inner )
325325 if isinstance (e , exp .DPipe ):
326- # The `||` string-concat operator: supported by DataFusion and native,
327- # deferred by codegen (its NULL-propagating semantics differ from CONCAT ).
328- raise UnsupportedInCodegen ( "the || operator is not supported in codegen yet" )
326+ # The `||` operator: NULL-propagating string concat (mirrors native
327+ # concat_op). Binary -- a||b||c nests as DPipe(DPipe(a,b), c ).
328+ return BinaryOp ( "dpipe" , _convert_expr ( e . this ), _convert_expr ( e . expression ) )
329329 if isinstance (e , exp .Not ):
330330 return Not (_convert_expr (e .this ))
331331 if isinstance (e , exp .Case ):
@@ -740,6 +740,8 @@ def infer_type(e: Any, schemas: dict) -> FieldType:
740740 if e .op in ("add" , "sub" , "mul" , "div" , "mod" ):
741741 base = INT if left .base == INT and right .base == INT else FLOAT
742742 return FieldType (base , nullable )
743+ if e .op == "dpipe" :
744+ return FieldType (STR , nullable )
743745 if is_container (left .base ) or is_container (right .base ):
744746 # Comparing/combining structs or lists needs deep equality, which
745747 # the runtime doesn't implement -- without this it silently reaches
0 commit comments