Skip to content

Commit 902c546

Browse files
committed
Merge branch 'main' into shuowei-job-history
2 parents 461ee48 + 34fb5da commit 902c546

File tree

13 files changed

+87
-23
lines changed

13 files changed

+87
-23
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):

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,10 +366,16 @@ def string_slice(
366366
column_length + sge.convert(start + 1),
367367
]
368368
)
369-
length_expr = sge.convert(op_end) - sge.Greatest(
369+
length_expr = sge.Greatest(
370370
expressions=[
371371
sge.convert(0),
372-
column_length + sge.convert(start),
372+
sge.convert(op_end)
373+
- sge.Greatest(
374+
expressions=[
375+
sge.convert(0),
376+
column_length + sge.convert(start),
377+
]
378+
),
373379
]
374380
)
375381
else:

tests/system/small/test_dataframe.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6296,3 +6296,20 @@ def test_agg_with_dict_containing_non_existing_col_raise_key_error(scalars_dfs):
62966296

62976297
with pytest.raises(KeyError):
62986298
bf_df.agg(agg_funcs)
6299+
6300+
6301+
def test_empty_agg_projection_succeeds():
6302+
# Tests that the compiler generates a SELECT 1 fallback for empty aggregations,
6303+
# protecting against BigQuery syntax errors when both groups and metrics are empty.
6304+
import importlib
6305+
6306+
bq = importlib.import_module(
6307+
"bigframes_vendored.ibis.backends.sql.compilers.bigquery"
6308+
)
6309+
sg = importlib.import_module("bigframes_vendored.sqlglot")
6310+
6311+
compiler = bq.BigQueryCompiler()
6312+
res = compiler.visit_Aggregate(
6313+
"op", parent=sg.table("parent_table"), groups=[], metrics=[]
6314+
)
6315+
assert "SELECT 1" in res.sql()

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/snapshots/test_string_ops/test_str_slice/out.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ SELECT
1313
SUBSTRING(
1414
`string_col`,
1515
GREATEST(1, LENGTH(`string_col`) + -2),
16-
5 - GREATEST(0, LENGTH(`string_col`) + -3)
16+
GREATEST(0, 5 - GREATEST(0, LENGTH(`string_col`) + -3))
1717
) AS `m3_5`
1818
FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0`

0 commit comments

Comments
 (0)