1313# limitations under the License.
1414
1515import dataclasses
16- from typing import cast
16+ from typing import Literal , cast
1717
1818import numpy as np
1919import pandas as pd
3636@dataclasses .dataclass
3737class CoerceArgsRule (op_lowering .OpLoweringRule ):
3838 op_type : type [ops .BinaryOp ]
39+ dialect : Literal ["polars" , "substrait" ]
3940
4041 @property
4142 def op (self ) -> type [ops .ScalarOp ]:
4243 return self .op_type
4344
4445 def lower (self , expr : expression .OpExpression ) -> expression .Expression :
4546 assert isinstance (expr .op , self .op_type )
46- larg , rarg = _coerce_comparables (expr .children [0 ], expr .children [1 ])
47+ larg , rarg = _coerce_comparables (
48+ expr .children [0 ], expr .children [1 ], dialect = self .dialect
49+ )
4750 return expr .op .as_expr (larg , rarg )
4851
4952
@@ -285,14 +288,20 @@ def lower(self, expr: expression.OpExpression) -> expression.Expression:
285288 return wo_bools
286289
287290
291+ @dataclasses .dataclass
288292class LowerAsTypeRule (op_lowering .OpLoweringRule ):
293+ dialect : Literal ["polars" , "substrait" ]
294+
289295 @property
290296 def op (self ) -> type [ops .ScalarOp ]:
291297 return ops .AsTypeOp
292298
293299 def lower (self , expr : expression .OpExpression ) -> expression .Expression :
294300 assert isinstance (expr .op , ops .AsTypeOp )
295- return _lower_cast (expr .op , expr .inputs [0 ])
301+ if self .dialect == "polars" :
302+ return _lower_cast_to_polars (expr .op , expr .inputs [0 ])
303+ elif self .dialect == "substrait" :
304+ return _lower_cast_to_substrait (expr .op , expr .inputs [0 ])
296305
297306
298307def invert_bytes (byte_string ):
@@ -392,6 +401,7 @@ def _coerce_comparables(
392401 expr2 : expression .Expression ,
393402 * ,
394403 bools_only : bool = False ,
404+ dialect : Literal ["polars" , "substrait" ],
395405):
396406 if bools_only :
397407 if (
@@ -402,13 +412,19 @@ def _coerce_comparables(
402412
403413 target_type = dtypes .coerce_to_common (expr1 .output_type , expr2 .output_type )
404414 if expr1 .output_type != target_type :
405- expr1 = _lower_cast (ops .AsTypeOp (target_type ), expr1 )
415+ if dialect == "polars" :
416+ expr1 = _lower_cast_to_polars (ops .AsTypeOp (target_type ), expr1 )
417+ elif dialect == "substrait" :
418+ expr1 = _lower_cast_to_substrait (ops .AsTypeOp (target_type ), expr1 )
406419 if expr2 .output_type != target_type :
407- expr2 = _lower_cast (ops .AsTypeOp (target_type ), expr2 )
420+ if dialect == "polars" :
421+ expr2 = _lower_cast_to_polars (ops .AsTypeOp (target_type ), expr2 )
422+ elif dialect == "substrait" :
423+ expr2 = _lower_cast_to_substrait (ops .AsTypeOp (target_type ), expr2 )
408424 return expr1 , expr2
409425
410426
411- def _lower_cast (cast_op : ops .AsTypeOp , arg : expression .Expression ):
427+ def _lower_cast_to_polars (cast_op : ops .AsTypeOp , arg : expression .Expression ):
412428 if arg .output_type == cast_op .to_type :
413429 return arg
414430 if (
@@ -435,8 +451,6 @@ def _lower_cast(cast_op: ops.AsTypeOp, arg: expression.Expression):
435451 ):
436452 return datetime_ops .StrftimeOp ("%Y-%m-%d %H:%M:%S%.6f%:::z" ).as_expr (arg )
437453 if arg .output_type == dtypes .BOOL_DTYPE and cast_op .to_type == dtypes .STRING_DTYPE :
438- # bool -> decimal needs two-step cast
439- new_arg = ops .AsTypeOp (to_type = dtypes .INT_DTYPE ).as_expr (arg )
440454 is_true_cond = ops .eq_op .as_expr (arg , expression .const (True ))
441455 is_false_cond = ops .eq_op .as_expr (arg , expression .const (False ))
442456 return ops .CaseWhenOp ().as_expr (
@@ -459,6 +473,19 @@ def _lower_cast(cast_op: ops.AsTypeOp, arg: expression.Expression):
459473 return cast_op .as_expr (arg )
460474
461475
476+ def _lower_cast_to_substrait (cast_op : ops .AsTypeOp , arg : expression .Expression ):
477+ if arg .output_type == dtypes .BOOL_DTYPE and cast_op .to_type == dtypes .STRING_DTYPE :
478+ is_true_cond = ops .eq_op .as_expr (arg , expression .const (True ))
479+ is_false_cond = ops .eq_op .as_expr (arg , expression .const (False ))
480+ return ops .CaseWhenOp ().as_expr (
481+ is_true_cond ,
482+ expression .const ("True" ),
483+ is_false_cond ,
484+ expression .const ("False" ),
485+ )
486+ return cast_op .as_expr (arg )
487+
488+
462489class LowerEqNullsMatchRule (op_lowering .OpLoweringRule ):
463490 @property
464491 def op (self ) -> type [ops .ScalarOp ]:
@@ -500,7 +527,7 @@ def lower(self, expr: expression.OpExpression) -> expression.Expression:
500527
501528
502529POLARS_LOWER_COMPARISONS = tuple (
503- CoerceArgsRule (op )
530+ CoerceArgsRule (op , dialect = "polars" )
504531 for op in (
505532 comparison_ops .EqOp ,
506533 comparison_ops .EqNullsMatchOp ,
@@ -513,7 +540,7 @@ def lower(self, expr: expression.OpExpression) -> expression.Expression:
513540)
514541
515542SUBSTRAIT_LOWER_COMPARISONS = tuple (
516- CoerceArgsRule (op )
543+ CoerceArgsRule (op , dialect = "substrait" )
517544 for op in (
518545 comparison_ops .EqOp ,
519546 comparison_ops .NeOp ,
@@ -532,7 +559,7 @@ def lower(self, expr: expression.OpExpression) -> expression.Expression:
532559 LowerDivRule (),
533560 LowerFloorDivRule (),
534561 LowerModRule (),
535- LowerAsTypeRule (),
562+ LowerAsTypeRule (dialect = "polars" ),
536563 LowerInvertOp (),
537564 LowerIsinOp (),
538565 LowerLenOp (),
@@ -543,6 +570,7 @@ def lower(self, expr: expression.OpExpression) -> expression.Expression:
543570SUBSTRAIT_LOWERING_RULES = (
544571 LowerEqNullsMatchRule (),
545572 * SUBSTRAIT_LOWER_COMPARISONS ,
573+ LowerAsTypeRule (dialect = "substrait" ),
546574)
547575
548576
0 commit comments