Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
This repository was archived by the owner on May 7, 2026. It is now read-only.

Commit 34fb5da

Browse filesBrowse files
authored
fix: handle aggregate operations on empty selections (#2510)
SQL generator output fallbacks (SELECT 1 placeholder). Fixes #<452681068> 🦕
1 parent b589de9 commit 34fb5da
Copy full SHA for 34fb5da

3 files changed

+37-3Lines changed: 37 additions & 3 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎tests/system/small/test_dataframe.py‎

Copy file name to clipboardExpand all lines: tests/system/small/test_dataframe.py
+17Lines 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()
Collapse file

‎third_party/bigframes_vendored/ibis/backends/sql/compilers/base.py‎

Copy file name to clipboardExpand all lines: third_party/bigframes_vendored/ibis/backends/sql/compilers/base.py
+11-3Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,9 +1394,17 @@ def _generate_groups(groups):
13941394
return map(sge.convert, range(1, len(groups) + 1))
13951395

13961396
def visit_Aggregate(self, op, *, parent, groups, metrics):
1397-
sel = sg.select(
1398-
*self._cleanup_names(groups), *self._cleanup_names(metrics), copy=False
1399-
).from_(parent, copy=False)
1397+
exprs = []
1398+
if groups:
1399+
exprs.extend(self._cleanup_names(groups))
1400+
if metrics:
1401+
exprs.extend(self._cleanup_names(metrics))
1402+
1403+
if not exprs:
1404+
# Empty aggregated projections are invalid in BigQuery
1405+
exprs = [sge.Literal.number(1)]
1406+
1407+
sel = sg.select(*exprs, copy=False).from_(parent, copy=False)
14001408

14011409
if groups:
14021410
sel = sel.group_by(*self._generate_groups(groups.values()), copy=False)
Collapse file

‎third_party/bigframes_vendored/ibis/backends/sql/compilers/bigquery/__init__.py‎

Copy file name to clipboardExpand all lines: third_party/bigframes_vendored/ibis/backends/sql/compilers/bigquery/__init__.py
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,15 @@ def visit_TimestampFromUNIX(self, op, *, arg, unit):
540540

541541
def visit_Cast(self, op, *, arg, to):
542542
from_ = op.arg.dtype
543+
if to.is_null():
544+
return sge.Null()
545+
if arg is NULL or (
546+
isinstance(arg, sge.Cast)
547+
and getattr(arg, "to", None) is not None
548+
and str(arg.to).upper() == "NULL"
549+
):
550+
if to.is_struct() or to.is_array():
551+
return sge.Cast(this=NULL, to=self.type_mapper.from_ibis(to))
543552
if from_.is_timestamp() and to.is_integer():
544553
return self.f.unix_micros(arg)
545554
elif from_.is_integer() and to.is_timestamp():

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.