From b1fcde8f76cbeea3a40e81fe2eebfc315ca9acc6 Mon Sep 17 00:00:00 2001 From: Phillip Cloud <417981+cpcloud@users.noreply.github.com> Date: Tue, 26 Sep 2023 06:23:46 -0400 Subject: [PATCH] fix(bigquery): quote struct field names in memtable when necessary --- ibis/backends/bigquery/compiler.py | 6 ++---- ibis/backends/bigquery/datatypes.py | 6 +++++- .../test_compiler/test_compile_in_memory_table/out.sql | 3 +++ 3 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 ibis/backends/bigquery/tests/unit/snapshots/test_compiler/test_compile_in_memory_table/out.sql diff --git a/ibis/backends/bigquery/compiler.py b/ibis/backends/bigquery/compiler.py index 2a3632428961..0c90dd9714ff 100644 --- a/ibis/backends/bigquery/compiler.py +++ b/ibis/backends/bigquery/compiler.py @@ -5,6 +5,7 @@ import re from functools import partial +import sqlglot as sg import toolz import ibis.common.graph as lin @@ -66,7 +67,6 @@ def find_bigquery_udf(op): _NAME_REGEX = re.compile(r'[^!"$()*,./;?@[\\\]^`{}~\n]+') -_EXACT_NAME_REGEX = re.compile(f"^{_NAME_REGEX.pattern}$") class BigQueryExprTranslator(sql_compiler.ExprTranslator): @@ -125,9 +125,7 @@ def _rewrite_notany(op): class BigQueryTableSetFormatter(sql_compiler.TableSetFormatter): def _quote_identifier(self, name): - if _EXACT_NAME_REGEX.match(name) is not None: - return name - return f"`{name}`" + return sg.to_identifier(name).sql("bigquery") def _format_in_memory_table(self, op): import ibis diff --git a/ibis/backends/bigquery/datatypes.py b/ibis/backends/bigquery/datatypes.py index 170cf18a7aad..2639f21ffb67 100644 --- a/ibis/backends/bigquery/datatypes.py +++ b/ibis/backends/bigquery/datatypes.py @@ -1,6 +1,7 @@ from __future__ import annotations import google.cloud.bigquery as bq +import sqlglot as sg import ibis.expr.datatypes as dt import ibis.expr.schema as sch @@ -76,7 +77,10 @@ def from_ibis(cls, dtype: dt.DataType) -> str: elif dtype.is_array(): return f"ARRAY<{cls.from_ibis(dtype.value_type)}>" elif dtype.is_struct(): - fields = (f"{k} {cls.from_ibis(v)}" for k, v in dtype.fields.items()) + fields = ( + f"{sg.to_identifier(k).sql('bigquery')} {cls.from_ibis(v)}" + for k, v in dtype.fields.items() + ) return "STRUCT<{}>".format(", ".join(fields)) elif dtype.is_json(): return "JSON" diff --git a/ibis/backends/bigquery/tests/unit/snapshots/test_compiler/test_compile_in_memory_table/out.sql b/ibis/backends/bigquery/tests/unit/snapshots/test_compiler/test_compile_in_memory_table/out.sql new file mode 100644 index 000000000000..a062c8b488da --- /dev/null +++ b/ibis/backends/bigquery/tests/unit/snapshots/test_compiler/test_compile_in_memory_table/out.sql @@ -0,0 +1,3 @@ +SELECT + t0.* +FROM UNNEST(ARRAY>[STRUCT(1 AS `Column One`), STRUCT(2 AS `Column One`), STRUCT(3 AS `Column One`)]) AS t0 \ No newline at end of file