Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit af492e5

Browse files
committed
refactor: apply is_null_literal
1 parent 44e0ffd commit af492e5

File tree

8 files changed

+41
-17
lines changed

8 files changed

+41
-17
lines changed

bigframes/core/compile/sqlglot/expressions/bool_ops.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from bigframes import dtypes
2020
from bigframes import operations as ops
21+
from bigframes.core.compile.sqlglot import sql
2122
import bigframes.core.compile.sqlglot.expression_compiler as expression_compiler
2223
from bigframes.core.compile.sqlglot.expressions.typed_expr import TypedExpr
2324

@@ -29,10 +30,10 @@ def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
2930
# For AND, when we encounter a NULL value, we only know when the result is FALSE,
3031
# otherwise the result is unknown (NULL). See: truth table at
3132
# https://en.wikibooks.org/wiki/Structured_Query_Language/NULLs_and_the_Three_Valued_Logic#AND,_OR
32-
if left.expr == sge.null():
33+
if sql.is_null_literal(left.expr):
3334
condition = sge.EQ(this=right.expr, expression=sge.convert(False))
3435
return sge.If(this=condition, true=right.expr, false=sge.null())
35-
if right.expr == sge.null():
36+
if sql.is_null_literal(right.expr):
3637
condition = sge.EQ(this=left.expr, expression=sge.convert(False))
3738
return sge.If(this=condition, true=left.expr, false=sge.null())
3839

@@ -46,10 +47,10 @@ def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
4647
# For OR, when we encounter a NULL value, we only know when the result is TRUE,
4748
# otherwise the result is unknown (NULL). See: truth table at
4849
# https://en.wikibooks.org/wiki/Structured_Query_Language/NULLs_and_the_Three_Valued_Logic#AND,_OR
49-
if left.expr == sge.null():
50+
if sql.is_null_literal(left.expr):
5051
condition = sge.EQ(this=right.expr, expression=sge.convert(True))
5152
return sge.If(this=condition, true=right.expr, false=sge.null())
52-
if right.expr == sge.null():
53+
if sql.is_null_literal(right.expr):
5354
condition = sge.EQ(this=left.expr, expression=sge.convert(True))
5455
return sge.If(this=condition, true=left.expr, false=sge.null())
5556

@@ -64,12 +65,12 @@ def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
6465
# maintains the boolean data type.
6566
left_expr = left.expr
6667
left_dtype = left.dtype
67-
if left_expr == sge.null():
68+
if sql.is_null_literal(left_expr):
6869
left_expr = sge.Cast(this=sge.convert(None), to="BOOLEAN")
6970
left_dtype = dtypes.BOOL_DTYPE
7071
right_expr = right.expr
7172
right_dtype = right.dtype
72-
if right_expr == sge.null():
73+
if sql.is_null_literal(right_expr):
7374
right_expr = sge.Cast(this=sge.convert(None), to="BOOLEAN")
7475
right_dtype = dtypes.BOOL_DTYPE
7576

bigframes/core/compile/sqlglot/expressions/comparison_ops.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
102102

103103
@register_binary_op(ops.ge_op)
104104
def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
105-
if left.expr == sge.null() or right.expr == sge.null():
105+
if sql.is_null_literal(left.expr) or sql.is_null_literal(right.expr):
106106
return sge.null()
107107

108108
left_expr = _coerce_bool_to_int(left)
@@ -112,7 +112,7 @@ def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
112112

113113
@register_binary_op(ops.gt_op)
114114
def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
115-
if left.expr == sge.null() or right.expr == sge.null():
115+
if sql.is_null_literal(left.expr) or sql.is_null_literal(right.expr):
116116
return sge.null()
117117

118118
left_expr = _coerce_bool_to_int(left)
@@ -122,7 +122,7 @@ def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
122122

123123
@register_binary_op(ops.lt_op)
124124
def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
125-
if left.expr == sge.null() or right.expr == sge.null():
125+
if sql.is_null_literal(left.expr) or sql.is_null_literal(right.expr):
126126
return sge.null()
127127

128128
left_expr = _coerce_bool_to_int(left)
@@ -132,7 +132,7 @@ def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
132132

133133
@register_binary_op(ops.le_op)
134134
def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
135-
if left.expr == sge.null() or right.expr == sge.null():
135+
if sql.is_null_literal(left.expr) or sql.is_null_literal(right.expr):
136136
return sge.null()
137137

138138
left_expr = _coerce_bool_to_int(left)

bigframes/core/compile/sqlglot/expressions/numeric_ops.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
from bigframes import dtypes
2121
from bigframes import operations as ops
22+
from bigframes.core.compile.sqlglot import sql
2223
import bigframes.core.compile.sqlglot.expression_compiler as expression_compiler
2324
from bigframes.core.compile.sqlglot.expressions.common import round_towards_zero
2425
import bigframes.core.compile.sqlglot.expressions.constants as constants
@@ -260,6 +261,9 @@ def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
260261
def _int_pow_op(
261262
left_expr: sge.Expression, right_expr: sge.Expression
262263
) -> sge.Expression:
264+
if sql.is_null_literal(left_expr) or sql.is_null_literal(right_expr):
265+
return sge.null()
266+
263267
overflow_cond = sge.and_(
264268
sge.NEQ(this=left_expr, expression=sge.convert(0)),
265269
sge.GT(
@@ -292,6 +296,9 @@ def _int_pow_op(
292296
def _float_pow_op(
293297
left_expr: sge.Expression, right_expr: sge.Expression
294298
) -> sge.Expression:
299+
if sql.is_null_literal(left_expr) or sql.is_null_literal(right_expr):
300+
return sge.null()
301+
295302
# Most conditions here seek to prevent calling BQ POW with inputs that would generate errors.
296303
# See: https://cloud.google.com/bigquery/docs/reference/standard-sql/mathematical_functions#pow
297304
overflow_cond = sge.and_(
@@ -425,7 +432,7 @@ def _(expr: TypedExpr) -> sge.Expression:
425432

426433
@register_binary_op(ops.add_op)
427434
def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
428-
if left.expr == sge.null() or right.expr == sge.null():
435+
if sql.is_null_literal(left.expr) or sql.is_null_literal(right.expr):
429436
return sge.null()
430437

431438
if left.dtype == dtypes.STRING_DTYPE and right.dtype == dtypes.STRING_DTYPE:
@@ -463,6 +470,9 @@ def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
463470

464471
@register_binary_op(ops.div_op)
465472
def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
473+
if sql.is_null_literal(left.expr) or sql.is_null_literal(right.expr):
474+
return sge.null()
475+
466476
left_expr = _coerce_bool_to_int(left)
467477
right_expr = _coerce_bool_to_int(right)
468478

@@ -482,7 +492,7 @@ def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
482492

483493
@register_binary_op(ops.floordiv_op)
484494
def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
485-
if left.expr == sge.null() or right.expr == sge.null():
495+
if sql.is_null_literal(left.expr) or sql.is_null_literal(right.expr):
486496
return sge.null()
487497

488498
left_expr = _coerce_bool_to_int(left)
@@ -525,6 +535,9 @@ def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
525535

526536
@register_binary_op(ops.mod_op)
527537
def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
538+
if sql.is_null_literal(left.expr) or sql.is_null_literal(right.expr):
539+
return sge.null()
540+
528541
# In BigQuery returned value has the same sign as X. In pandas, the sign of y is used, so we need to flip the result if sign(x) != sign(y)
529542
left_expr = _coerce_bool_to_int(left)
530543
right_expr = _coerce_bool_to_int(right)
@@ -568,7 +581,7 @@ def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
568581

569582
@register_binary_op(ops.mul_op)
570583
def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
571-
if left.expr == sge.null() or right.expr == sge.null():
584+
if sql.is_null_literal(left.expr) or sql.is_null_literal(right.expr):
572585
return sge.null()
573586

574587
left_expr = _coerce_bool_to_int(left)
@@ -594,7 +607,7 @@ def _(expr: TypedExpr, n_digits: TypedExpr) -> sge.Expression:
594607

595608
@register_binary_op(ops.sub_op)
596609
def _(left: TypedExpr, right: TypedExpr) -> sge.Expression:
597-
if left.expr == sge.null() or right.expr == sge.null():
610+
if sql.is_null_literal(left.expr) or sql.is_null_literal(right.expr):
598611
return sge.null()
599612

600613
if dtypes.is_numeric(left.dtype) and dtypes.is_numeric(right.dtype):

tests/unit/core/compile/sqlglot/expressions/snapshots/test_numeric_ops/test_div_numeric/out.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ SELECT
66
IEEE_DIVIDE(`int64_col`, `int64_col`) AS `int_div_int`,
77
IEEE_DIVIDE(`int64_col`, 1) AS `int_div_1`,
88
IEEE_DIVIDE(`int64_col`, 0.0) AS `int_div_0`,
9-
IEEE_DIVIDE(`int64_col`, NULL) AS `int_div_null`,
9+
NULL AS `int_div_null`,
1010
IEEE_DIVIDE(`int64_col`, `float64_col`) AS `int_div_float`,
1111
IEEE_DIVIDE(`float64_col`, `int64_col`) AS `float_div_int`,
1212
IEEE_DIVIDE(`float64_col`, 0.0) AS `float_div_0`,

tests/unit/core/compile/sqlglot/expressions/snapshots/test_numeric_ops/test_floordiv_numeric/out.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ SELECT
3434
THEN CAST('Infinity' AS FLOAT64) * `float64_col`
3535
ELSE CAST(FLOOR(IEEE_DIVIDE(`float64_col`, 0.0)) AS INT64)
3636
END AS `float_div_0`,
37+
NULL AS `float_div_null`,
3738
CASE
3839
WHEN CAST(`bool_col` AS INT64) = CAST(0 AS INT64)
3940
THEN CAST(0 AS INT64) * `int64_col`

tests/unit/core/compile/sqlglot/expressions/snapshots/test_numeric_ops/test_mod_numeric/out.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,5 +189,6 @@ SELECT
189189
MOD(CAST(`float64_col` AS BIGNUMERIC), CAST(0 AS BIGNUMERIC))
190190
)
191191
ELSE MOD(CAST(`float64_col` AS BIGNUMERIC), CAST(0 AS BIGNUMERIC))
192-
END AS `float_mod_0`
192+
END AS `float_mod_0`,
193+
NULL AS `float_mod_null`
193194
FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0`

tests/unit/core/compile/sqlglot/expressions/snapshots/test_numeric_ops/test_pow/out.sql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,5 +241,7 @@ SELECT
241241
ELSE 1
242242
END
243243
)
244-
END AS `float_pow_1`
244+
END AS `float_pow_1`,
245+
NULL AS `float_pow_null`,
246+
NULL AS `null_pow_float`
245247
FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0`

tests/unit/core/compile/sqlglot/expressions/test_numeric_ops.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@ def test_pow(scalar_types_df: bpd.DataFrame, snapshot):
220220
bf_df["int_pow_1"] = bf_df["int64_col"] ** 1
221221
bf_df["float_pow_1"] = bf_df["float64_col"] ** 1
222222

223+
bf_df["float_pow_null"] = bf_df["float64_col"] ** pd.NA
224+
bf_df["null_pow_float"] = pd.NA ** bf_df["float64_col"]
225+
223226
snapshot.assert_match(bf_df.sql, "out.sql")
224227

225228

@@ -370,6 +373,7 @@ def test_floordiv_numeric(scalar_types_df: bpd.DataFrame, snapshot):
370373
bf_df["int_div_float"] = bf_df["int64_col"] // bf_df["float64_col"]
371374
bf_df["float_div_int"] = bf_df["float64_col"] // bf_df["int64_col"]
372375
bf_df["float_div_0"] = bf_df["float64_col"] // 0.0
376+
bf_df["float_div_null"] = bf_df["float64_col"] // pd.NA
373377

374378
bf_df["int_div_bool"] = bf_df["int64_col"] // bf_df["bool_col"]
375379
bf_df["bool_div_int"] = bf_df["bool_col"] // bf_df["int64_col"]
@@ -437,6 +441,8 @@ def test_mod_numeric(scalar_types_df: bpd.DataFrame, snapshot):
437441
bf_df["float_mod_1"] = bf_df["float64_col"] % 1
438442
bf_df["float_mod_0"] = bf_df["float64_col"] % 0
439443

444+
bf_df["float_mod_null"] = bf_df["float64_col"] % pd.NA
445+
440446
snapshot.assert_match(bf_df.sql, "out.sql")
441447

442448

0 commit comments

Comments
 (0)