Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 31 additions & 4 deletions bigframes/core/compile/sqlglot/expressions/bool_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,49 @@

@register_binary_op(ops.and_op)
def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
# For AND, when we encounter a NULL value, we only know when the result is FALSE,
# otherwise the result is unknown (NULL). See: truth table at
# https://en.wikibooks.org/wiki/Structured_Query_Language/NULLs_and_the_Three_Valued_Logic#AND,_OR
if left.expr == sge.null():
condition = sge.EQ(this=right.expr, expression=sge.convert(False))
return sge.If(this=condition, true=right.expr, false=sge.null())
if right.expr == sge.null():
condition = sge.EQ(this=left.expr, expression=sge.convert(False))
return sge.If(this=condition, true=left.expr, false=sge.null())

if left.dtype == dtypes.BOOL_DTYPE and right.dtype == dtypes.BOOL_DTYPE:
return sge.And(this=left.expr, expression=right.expr)
return sge.BitwiseAnd(this=left.expr, expression=right.expr)


@register_binary_op(ops.or_op)
def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
# For OR, when we encounter a NULL value, we only know when the result is TRUE,
# otherwise the result is unknown (NULL). See: truth table at
# https://en.wikibooks.org/wiki/Structured_Query_Language/NULLs_and_the_Three_Valued_Logic#AND,_OR
if left.expr == sge.null():
condition = sge.EQ(this=right.expr, expression=sge.convert(True))
return sge.If(this=condition, true=right.expr, false=sge.null())
if right.expr == sge.null():
condition = sge.EQ(this=left.expr, expression=sge.convert(True))
return sge.If(this=condition, true=left.expr, false=sge.null())

if left.dtype == dtypes.BOOL_DTYPE and right.dtype == dtypes.BOOL_DTYPE:
return sge.Or(this=left.expr, expression=right.expr)
return sge.BitwiseOr(this=left.expr, expression=right.expr)


@register_binary_op(ops.xor_op)
def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
if left.dtype == dtypes.BOOL_DTYPE and right.dtype == dtypes.BOOL_DTYPE:
left_expr = sge.And(this=left.expr, expression=sge.Not(this=right.expr))
right_expr = sge.And(this=sge.Not(this=left.expr), expression=right.expr)
return sge.Or(this=left_expr, expression=right_expr)
left_expr = left.expr
if left_expr == sge.null():
left_expr = sge.Cast(this=sge.convert(None), to="BOOLEAN")
right_expr = right.expr
if right_expr == sge.null():
right_expr = sge.Cast(this=sge.convert(None), to="BOOLEAN")

if left.dtype == dtypes.BOOL_DTYPE or right.dtype == dtypes.BOOL_DTYPE:
left_expr = sge.And(this=left_expr, expression=sge.Not(this=right_expr))
right_expr = sge.And(this=sge.Not(this=left_expr), expression=right_expr)
return sge.Or(this=sge.paren(left_expr), expression=sge.paren(right_expr))
return sge.BitwiseXor(this=left.expr, expression=right.expr)
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,22 @@ WITH `bfcte_0` AS (
`bfcol_9` AS `bfcol_17`,
`bfcol_7` AND `bfcol_7` AS `bfcol_18`
FROM `bfcte_1`
), `bfcte_3` AS (
SELECT
*,
`bfcol_14` AS `bfcol_24`,
`bfcol_15` AS `bfcol_25`,
`bfcol_16` AS `bfcol_26`,
`bfcol_17` AS `bfcol_27`,
`bfcol_18` AS `bfcol_28`,
IF(`bfcol_15` = FALSE, `bfcol_15`, NULL) AS `bfcol_29`
FROM `bfcte_2`
)
SELECT
`bfcol_14` AS `rowindex`,
`bfcol_15` AS `bool_col`,
`bfcol_16` AS `int64_col`,
`bfcol_17` AS `int_and_int`,
`bfcol_18` AS `bool_and_bool`
FROM `bfcte_2`
`bfcol_24` AS `rowindex`,
`bfcol_25` AS `bool_col`,
`bfcol_26` AS `int64_col`,
`bfcol_27` AS `int_and_int`,
`bfcol_28` AS `bool_and_bool`,
`bfcol_29` AS `bool_and_null`
FROM `bfcte_3`
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,22 @@ WITH `bfcte_0` AS (
`bfcol_9` AS `bfcol_17`,
`bfcol_7` OR `bfcol_7` AS `bfcol_18`
FROM `bfcte_1`
), `bfcte_3` AS (
SELECT
*,
`bfcol_14` AS `bfcol_24`,
`bfcol_15` AS `bfcol_25`,
`bfcol_16` AS `bfcol_26`,
`bfcol_17` AS `bfcol_27`,
`bfcol_18` AS `bfcol_28`,
IF(`bfcol_15` = TRUE, `bfcol_15`, NULL) AS `bfcol_29`
FROM `bfcte_2`
)
SELECT
`bfcol_14` AS `rowindex`,
`bfcol_15` AS `bool_col`,
`bfcol_16` AS `int64_col`,
`bfcol_17` AS `int_and_int`,
`bfcol_18` AS `bool_and_bool`
FROM `bfcte_2`
`bfcol_24` AS `rowindex`,
`bfcol_25` AS `bool_col`,
`bfcol_26` AS `int64_col`,
`bfcol_27` AS `int_and_int`,
`bfcol_28` AS `bool_and_bool`,
`bfcol_29` AS `bool_and_null`
FROM `bfcte_3`
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,34 @@ WITH `bfcte_0` AS (
`bfcol_7` AS `bfcol_15`,
`bfcol_8` AS `bfcol_16`,
`bfcol_9` AS `bfcol_17`,
`bfcol_7` AND NOT `bfcol_7` OR NOT `bfcol_7` AND `bfcol_7` AS `bfcol_18`
(
`bfcol_7` AND NOT `bfcol_7`
)
OR (
NOT `bfcol_7` AND NOT `bfcol_7` AND `bfcol_7`
) AS `bfcol_18`
FROM `bfcte_1`
), `bfcte_3` AS (
SELECT
*,
`bfcol_14` AS `bfcol_24`,
`bfcol_15` AS `bfcol_25`,
`bfcol_16` AS `bfcol_26`,
`bfcol_17` AS `bfcol_27`,
`bfcol_18` AS `bfcol_28`,
(
`bfcol_15` AND NOT CAST(NULL AS BOOLEAN)
)
OR (
NOT `bfcol_15` AND NOT CAST(NULL AS BOOLEAN) AND CAST(NULL AS BOOLEAN)
) AS `bfcol_29`
FROM `bfcte_2`
)
SELECT
`bfcol_14` AS `rowindex`,
`bfcol_15` AS `bool_col`,
`bfcol_16` AS `int64_col`,
`bfcol_17` AS `int_and_int`,
`bfcol_18` AS `bool_and_bool`
FROM `bfcte_2`
`bfcol_24` AS `rowindex`,
`bfcol_25` AS `bool_col`,
`bfcol_26` AS `int64_col`,
`bfcol_27` AS `int_and_int`,
`bfcol_28` AS `bool_and_bool`,
`bfcol_29` AS `bool_and_null`
FROM `bfcte_3`
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import pandas as pd
import pytest

import bigframes.pandas as bpd
Expand All @@ -24,6 +25,7 @@ def test_and_op(scalar_types_df: bpd.DataFrame, snapshot):

bf_df["int_and_int"] = bf_df["int64_col"] & bf_df["int64_col"]
bf_df["bool_and_bool"] = bf_df["bool_col"] & bf_df["bool_col"]
bf_df["bool_and_null"] = bf_df["bool_col"] & pd.NA
snapshot.assert_match(bf_df.sql, "out.sql")


Expand All @@ -32,6 +34,7 @@ def test_or_op(scalar_types_df: bpd.DataFrame, snapshot):

bf_df["int_and_int"] = bf_df["int64_col"] | bf_df["int64_col"]
bf_df["bool_and_bool"] = bf_df["bool_col"] | bf_df["bool_col"]
bf_df["bool_and_null"] = bf_df["bool_col"] | pd.NA
snapshot.assert_match(bf_df.sql, "out.sql")


Expand All @@ -40,4 +43,5 @@ def test_xor_op(scalar_types_df: bpd.DataFrame, snapshot):

bf_df["int_and_int"] = bf_df["int64_col"] ^ bf_df["int64_col"]
bf_df["bool_and_bool"] = bf_df["bool_col"] ^ bf_df["bool_col"]
bf_df["bool_and_null"] = bf_df["bool_col"] ^ pd.NA
snapshot.assert_match(bf_df.sql, "out.sql")
Loading