|
26 | 26 |
|
27 | 27 | @register_binary_op(ops.and_op) |
28 | 28 | def _(left: TypedExpr, right: TypedExpr) -> sge.Expression: |
| 29 | + # For AND, when we encounter a NULL value, we only know when the result is FALSE, |
| 30 | + # otherwise the result is unknown (NULL). See: truth table at |
| 31 | + # https://en.wikibooks.org/wiki/Structured_Query_Language/NULLs_and_the_Three_Valued_Logic#AND,_OR |
| 32 | + if left.expr == sge.null(): |
| 33 | + condition = sge.EQ(this=right.expr, expression=sge.convert(False)) |
| 34 | + return sge.If(this=condition, true=right.expr, false=sge.null()) |
| 35 | + if right.expr == sge.null(): |
| 36 | + condition = sge.EQ(this=left.expr, expression=sge.convert(False)) |
| 37 | + return sge.If(this=condition, true=left.expr, false=sge.null()) |
| 38 | + |
29 | 39 | if left.dtype == dtypes.BOOL_DTYPE and right.dtype == dtypes.BOOL_DTYPE: |
30 | 40 | return sge.And(this=left.expr, expression=right.expr) |
31 | 41 | return sge.BitwiseAnd(this=left.expr, expression=right.expr) |
32 | 42 |
|
33 | 43 |
|
34 | 44 | @register_binary_op(ops.or_op) |
35 | 45 | def _(left: TypedExpr, right: TypedExpr) -> sge.Expression: |
| 46 | + # For OR, when we encounter a NULL value, we only know when the result is TRUE, |
| 47 | + # otherwise the result is unknown (NULL). See: truth table at |
| 48 | + # https://en.wikibooks.org/wiki/Structured_Query_Language/NULLs_and_the_Three_Valued_Logic#AND,_OR |
| 49 | + if left.expr == sge.null(): |
| 50 | + condition = sge.EQ(this=right.expr, expression=sge.convert(True)) |
| 51 | + return sge.If(this=condition, true=right.expr, false=sge.null()) |
| 52 | + if right.expr == sge.null(): |
| 53 | + condition = sge.EQ(this=left.expr, expression=sge.convert(True)) |
| 54 | + return sge.If(this=condition, true=left.expr, false=sge.null()) |
| 55 | + |
36 | 56 | if left.dtype == dtypes.BOOL_DTYPE and right.dtype == dtypes.BOOL_DTYPE: |
37 | 57 | return sge.Or(this=left.expr, expression=right.expr) |
38 | 58 | return sge.BitwiseOr(this=left.expr, expression=right.expr) |
39 | 59 |
|
40 | 60 |
|
41 | 61 | @register_binary_op(ops.xor_op) |
42 | 62 | def _(left: TypedExpr, right: TypedExpr) -> sge.Expression: |
43 | | - if left.dtype == dtypes.BOOL_DTYPE and right.dtype == dtypes.BOOL_DTYPE: |
44 | | - left_expr = sge.And(this=left.expr, expression=sge.Not(this=right.expr)) |
45 | | - right_expr = sge.And(this=sge.Not(this=left.expr), expression=right.expr) |
46 | | - return sge.Or(this=left_expr, expression=right_expr) |
| 63 | + # For XOR, cast NULL operands to BOOLEAN to ensure the resulting expression |
| 64 | + # maintains the boolean data type. |
| 65 | + left_expr = left.expr |
| 66 | + left_dtype = left.dtype |
| 67 | + if left_expr == sge.null(): |
| 68 | + left_expr = sge.Cast(this=sge.convert(None), to="BOOLEAN") |
| 69 | + left_dtype = dtypes.BOOL_DTYPE |
| 70 | + right_expr = right.expr |
| 71 | + right_dtype = right.dtype |
| 72 | + if right_expr == sge.null(): |
| 73 | + right_expr = sge.Cast(this=sge.convert(None), to="BOOLEAN") |
| 74 | + right_dtype = dtypes.BOOL_DTYPE |
| 75 | + |
| 76 | + if left_dtype == dtypes.BOOL_DTYPE and right_dtype == dtypes.BOOL_DTYPE: |
| 77 | + return sge.Or( |
| 78 | + this=sge.paren( |
| 79 | + sge.And(this=left_expr, expression=sge.Not(this=right_expr)) |
| 80 | + ), |
| 81 | + expression=sge.paren( |
| 82 | + sge.And(this=sge.Not(this=left_expr), expression=right_expr) |
| 83 | + ), |
| 84 | + ) |
47 | 85 | return sge.BitwiseXor(this=left.expr, expression=right.expr) |
0 commit comments